public static void writeJavaAnnotationValue(ToolPageContext page, Object value)
        throws IOException {

      if (value instanceof String) {
        page.writeStart("code");
        page.writeHtml('"');
        page.writeHtml(value);
        page.writeHtml('"');
        page.writeEnd();

      } else if (value instanceof Class) {
        page.writeJavaClassLink((Class<?>) value);

      } else if (value instanceof Annotation) {
        writeJavaAnnotationDescription(page, (Annotation) value);

      } else if (value.getClass().isArray()) {
        int length = Array.getLength(value);

        if (length > 0) {
          page.writeStart("ol");
          for (int i = 0; i < length; ++i) {
            page.writeStart("li");
            writeJavaAnnotationValue(page, Array.get(value, i));
            page.writeEnd();
          }
          page.writeEnd();
        }

      } else {
        page.writeStart("code");
        page.writeHtml(value);
        page.writeEnd();
      }
    }
    private static void writeJavaAnnotationDescription(ToolPageContext page, Annotation annotation)
        throws IOException {

      Class<? extends Annotation> aClass = annotation.annotationType();

      page.writeJavaClassLink(aClass);

      List<Method> aMethods = new ArrayList<Method>(Arrays.asList(aClass.getMethods()));

      for (Iterator<Method> i = aMethods.iterator(); i.hasNext(); ) {
        if (!i.next().getDeclaringClass().equals(aClass)) {
          i.remove();
        }
      }

      if (!aMethods.isEmpty()) {
        page.writeStart("table", "class", "table-striped");
        page.writeStart("tbody");
        for (Method m : aMethods) {
          if (m.getDeclaringClass().equals(aClass)) {
            page.writeStart("tr");
            page.writeStart("th");
            page.writeStart("code");
            page.writeHtml(m.getName());
            page.writeEnd();
            page.writeEnd();

            page.writeStart("td");
            try {
              writeJavaAnnotationValue(page, m.invoke(annotation));
            } catch (IllegalAccessException error) {
              // Ignore reflection look-up errors.
            } catch (InvocationTargetException error) {
              // Ignore reflection look-up errors.
            }
            page.writeEnd();
            page.writeEnd();
          }
        }
        page.writeEnd();
        page.writeEnd();
      }
    }
    public static void writeJavaAnnotationDescriptions(
        ToolPageContext page, AnnotatedElement annotated) throws IOException {

      List<Annotation> presentAnnotations = Arrays.asList(annotated.getAnnotations());
      List<Class<? extends Annotation>> possibleAnnotationClasses =
          new ArrayList<Class<? extends Annotation>>();

      for (Class<? extends Annotation> ac : ClassFinder.Static.findClasses(Annotation.class)) {
        if (!ac.isAnnotationPresent(Deprecated.class)
            && ac.isAnnotationPresent(
                annotated instanceof Field
                    ? ObjectField.AnnotationProcessorClass.class
                    : ObjectType.AnnotationProcessorClass.class)) {
          possibleAnnotationClasses.add(ac);
          continue;
        }
      }

      Collections.sort(
          possibleAnnotationClasses,
          new Comparator<Class<? extends Annotation>>() {

            @Override
            public int compare(Class<? extends Annotation> x, Class<? extends Annotation> y) {
              return x.getName().compareTo(y.getName());
            }
          });

      if (!presentAnnotations.isEmpty()) {
        page.writeStart("h2");
        page.writeHtml("Present Annotations");
        page.writeEnd();

        page.writeStart("ul");
        for (Annotation a : presentAnnotations) {
          page.writeStart("li");
          writeJavaAnnotationDescription(page, a);
          page.writeEnd();
        }
        page.writeEnd();
      }

      page.writeStart("h2");
      page.writeHtml("Possible Annotations");
      page.writeEnd();

      page.writeStart("ul");
      for (Class<? extends Annotation> ac : possibleAnnotationClasses) {
        page.writeStart("li");
        page.writeJavaClassLink(ac);
        page.writeEnd();
      }
      page.writeEnd();
    }
  protected void writeTableHtml(Iterable<?> items) throws IOException {
    HttpServletRequest request = page.getRequest();

    page.putOverride(
        Recordable.class,
        (HtmlWriter writer, Recordable object) -> {
          ToolPageContext page = (ToolPageContext) writer;

          page.writeObjectLabel(object);
        });

    page.putOverride(
        Metric.class,
        (HtmlWriter writer, Metric object) -> {
          writer.write(Double.toString(object.getSum()));
        });

    page.putOverride(
        Content.class,
        (HtmlWriter writer, Content content) -> {
          ToolPageContext page = (ToolPageContext) writer;

          page.writeStart(
              "a", "href", page.objectUrl("/content/edit.jsp", content), "target", "_top");
          page.writeObjectLabel(content);
          page.writeEnd();
        });

    page.putOverride(
        StorageItem.class,
        (HtmlWriter writer, StorageItem item) -> {
          ToolPageContext page = (ToolPageContext) writer;

          page.writeElement(
              "img",
              "height",
              100,
              "src",
              ImageEditor.Static.getDefault() != null
                  ? new ImageTag.Builder(item).setHeight(100).toUrl()
                  : item.getPublicUrl());
        });

    page.writeStart("table", "class", "searchResultTable links table-striped pageThumbnails");
    page.writeStart("thead");
    page.writeStart("tr");
    page.writeStart("th");
    page.writeElement("input", "type", "checkbox", "class", "searchResult-checkAll");
    page.writeEnd();

    if (sortField != null && ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

      page.writeStart("th", "colspan", 2);
      page.writeHtml(sortField.getDisplayName());
      page.writeEnd();
    }

    if (showSiteLabel) {
      page.writeStart("th");
      page.writeHtml(page.localize(ListSearchResultView.class, "label.site"));
      page.writeEnd();
    }

    if (showTypeLabel) {
      page.writeStart("th");
      page.writeHtml(page.localize(ListSearchResultView.class, "label.type"));
      page.writeEnd();
    }

    page.writeStart("th");
    page.writeHtml(page.localize(ListSearchResultView.class, "label.label"));
    page.writeEnd();

    if (sortField != null && !ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

      page.writeStart("th");
      page.writeHtml(sortField.getDisplayName());
      page.writeEnd();
    }

    ToolUser user = page.getUser();

    if (user != null) {
      ObjectType selectedType = search.getSelectedType();
      List<String> fieldNames =
          user.getSearchResultFieldsByTypeId()
              .get(selectedType != null ? selectedType.getId().toString() : "");

      if (fieldNames == null) {
        for (Class<? extends SearchResultField> c :
            ClassFinder.Static.findClasses(SearchResultField.class)) {
          if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) {
            SearchResultField field = TypeDefinition.getInstance(c).newInstance();

            if (field.isDefault(selectedType) && !ObjectUtils.equals(sortField, field)) {
              field.writeTableHeaderCellHtml(page);
            }
          }
        }

        if (selectedType != null) {
          for (ObjectField field : selectedType.getFields()) {
            if (Boolean.TRUE.equals(field.as(ToolUi.class).getDefaultSearchResult())) {
              writeHeaderCell(field);
            }
          }
        }

      } else {
        for (String fieldName : fieldNames) {
          Class<?> fieldNameClass = ObjectUtils.getClassByName(fieldName);

          if (fieldNameClass != null && SearchResultField.class.isAssignableFrom(fieldNameClass)) {
            @SuppressWarnings("unchecked")
            SearchResultField field =
                TypeDefinition.getInstance((Class<? extends SearchResultField>) fieldNameClass)
                    .newInstance();

            if (field.isSupported(selectedType) && !ObjectUtils.equals(sortField, field)) {
              field.writeTableHeaderCellHtml(page);
            }

          } else {
            ObjectField field = null;
            if (selectedType != null) {
              field = selectedType.getField(fieldName);
            }

            if (field == null) {
              field = Database.Static.getDefault().getEnvironment().getField(fieldName);
            }

            if (field != null && !ObjectUtils.equals(sortField, field)) {
              writeHeaderCell(field);
            }
          }
        }
      }
    }
    page.writeEnd();
    page.writeEnd();

    page.writeStart("tbody");
    for (Object item : items) {
      State itemState = State.getInstance(item);
      String permalink = itemState.as(Directory.ObjectModification.class).getPermalink();
      Integer embedWidth = null;

      if (ObjectUtils.isBlank(permalink)) {
        ObjectType type = itemState.getType();

        if (type != null) {
          Renderer.TypeModification rendererData = type.as(Renderer.TypeModification.class);
          int previewWidth = rendererData.getEmbedPreviewWidth();

          if (previewWidth > 0 && !ObjectUtils.isBlank(rendererData.getEmbedPath())) {

            permalink = "/_preview?_embed=true&_cms.db.previewId=" + itemState.getId();
            embedWidth = 320;
          }
        }
      }

      page.writeStart(
          "tr",
          "data-preview-url",
          permalink,
          "data-preview-embed-width",
          embedWidth,
          "class",
          State.getInstance(item).getId().equals(page.param(UUID.class, "id")) ? "selected" : null);

      page.writeStart("td");
      itemWriter.writeCheckboxHtml(page, search, item);
      page.writeEnd();

      if (sortField != null && ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

        DateTime dateTime = page.toUserDateTime(itemState.getByPath(sortField.getInternalName()));

        if (dateTime == null) {
          page.writeStart("td", "colspan", 2);
          page.writeHtml("N/A");
          page.writeEnd();

        } else {
          String date = page.formatUserDate(dateTime);

          page.writeStart("td", "class", "date");
          if (!ObjectUtils.equals(date, request.getAttribute(PREVIOUS_DATE_ATTRIBUTE))) {
            request.setAttribute(PREVIOUS_DATE_ATTRIBUTE, date);
            page.writeHtml(date);
          }
          page.writeEnd();

          page.writeStart("td", "class", "time");
          page.writeHtml(page.formatUserTime(dateTime));
          page.writeEnd();
        }
      }

      if (showSiteLabel) {
        page.writeStart("td");
        page.writeObjectLabel(itemState.as(Site.ObjectModification.class).getOwner());
        page.writeEnd();
      }

      if (showTypeLabel) {
        page.writeStart("td");
        page.writeTypeLabel(item);
        page.writeEnd();
      }

      page.writeStart("td", "data-preview-anchor", "");
      itemWriter.writeBeforeHtml(page, search, item);
      page.writeObjectLabel(item);
      itemWriter.writeAfterHtml(page, search, item);
      page.writeEnd();

      if (sortField != null && !ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

        String sortFieldName = sortField.getInternalName();
        Object value = itemState.getByPath(sortFieldName);

        page.writeStart("td");
        if (value instanceof Metric) {
          page.writeStart("span", "style", page.cssString("white-space", "nowrap"));
          Double maxSum = (Double) request.getAttribute(MAX_SUM_ATTRIBUTE);

          if (maxSum == null) {
            Object maxObject = search.toQuery(page.getSite()).sortDescending(sortFieldName).first();
            maxSum =
                maxObject != null
                    ? ((Metric) State.getInstance(maxObject).get(sortFieldName)).getSum()
                    : 1.0;

            request.setAttribute(MAX_SUM_ATTRIBUTE, maxSum);
          }

          Metric valueMetric = (Metric) value;
          Map<DateTime, Double> sumEntries =
              valueMetric.groupSumByDate(
                  new MetricInterval.Daily(),
                  new DateTime().dayOfMonth().roundFloorCopy().minusDays(7),
                  null);

          double sum = valueMetric.getSum();
          long sumLong = (long) sum;

          if (sumLong == sum) {
            page.writeHtml(String.format("%,2d ", sumLong));

          } else {
            page.writeHtml(String.format("%,2.2f ", sum));
          }

          if (!sumEntries.isEmpty()) {
            long minMillis = Long.MAX_VALUE;
            long maxMillis = Long.MIN_VALUE;

            for (Map.Entry<DateTime, Double> sumEntry : sumEntries.entrySet()) {
              long sumMillis = sumEntry.getKey().getMillis();

              if (sumMillis < minMillis) {
                minMillis = sumMillis;
              }

              if (sumMillis > maxMillis) {
                maxMillis = sumMillis;
              }
            }

            double cumulativeSum = 0.0;
            StringBuilder path = new StringBuilder();
            double xRange = maxMillis - minMillis;
            int width = 35;
            int height = 18;

            for (Map.Entry<DateTime, Double> sumEntry : sumEntries.entrySet()) {
              cumulativeSum += sumEntry.getValue();

              path.append('L');
              path.append((sumEntry.getKey().getMillis() - minMillis) / xRange * width);
              path.append(',');
              path.append(height - cumulativeSum / maxSum * height);
            }

            path.setCharAt(0, 'M');

            page.writeStart(
                "svg",
                "xmlns",
                "http://www.w3.org/2000/svg",
                "width",
                width,
                "height",
                height,
                "style",
                page.cssString(
                    "display", "inline-block",
                    "vertical-align", "middle"));

            page.writeStart("path", "fill", "none", "stroke", "#444444", "d", path.toString());
            page.writeEnd();
            page.writeEnd();
          }
          page.writeEnd();

        } else if (value instanceof Recordable) {
          page.writeHtml(((Recordable) value).getState().getLabel());

        } else {
          for (Iterator<Object> i = CollectionUtils.recursiveIterable(value).iterator();
              i.hasNext(); ) {
            Object collectionValue = i.next();

            page.writeObject(collectionValue);

            if (i.hasNext()) {
              page.writeHtml(", ");
            }
          }
        }
        page.writeEnd();
      }

      if (user != null) {
        ObjectType selectedType = search.getSelectedType();
        List<String> fieldNames =
            user.getSearchResultFieldsByTypeId()
                .get(selectedType != null ? selectedType.getId().toString() : "");
        ObjectType itemType = itemState.getType();

        if (fieldNames == null) {
          for (Class<? extends SearchResultField> c :
              ClassFinder.Static.findClasses(SearchResultField.class)) {
            if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) {
              SearchResultField field = TypeDefinition.getInstance(c).newInstance();

              if (field.isDefault(itemType) && !ObjectUtils.equals(sortField, field)) {
                field.writeTableDataCellHtml(page, item);
              }
            }
          }

          if (selectedType != null) {
            for (ObjectField field : selectedType.getFields()) {
              if (Boolean.TRUE.equals(field.as(ToolUi.class).getDefaultSearchResult())) {
                writeDataCell(itemState, field.getInternalName());
              }
            }
          }

        } else {
          for (String fieldName : fieldNames) {
            Class<?> fieldNameClass = ObjectUtils.getClassByName(fieldName);

            if (fieldNameClass != null
                && SearchResultField.class.isAssignableFrom(fieldNameClass)) {
              @SuppressWarnings("unchecked")
              SearchResultField field =
                  TypeDefinition.getInstance((Class<? extends SearchResultField>) fieldNameClass)
                      .newInstance();

              if (field.isSupported(itemState.getType()) && !ObjectUtils.equals(sortField, field)) {
                field.writeTableDataCellHtml(page, item);
              }

            } else {
              ObjectField field = itemState.getField(fieldName);

              if (field == null) {
                field = Database.Static.getDefault().getEnvironment().getField(fieldName);
              }

              if (field != null && !ObjectUtils.equals(sortField, field)) {
                writeDataCell(itemState, fieldName);
              }
            }
          }
        }
      }
      page.writeEnd();
    }
    page.writeEnd();
    page.writeEnd();

    page.writeStart("div", "id", page.createId());
    page.writeEnd();

    page.writeStart("script", "type", "text/javascript");
    page.writeRaw(
        "$('#" + page.getId() + "').siblings('.searchResultTable').find('tr').each(function() {");
    page.writeRaw("    if ($(this).find('td > a').size() > 1) {");
    page.writeRaw(
        "        $(this).closest('.searchResultTable').addClass('multipleLinkedColumns');");
    page.writeRaw("        return false;");
    page.writeRaw("    }");
    page.writeRaw(" });");
    page.writeEnd();
  }
  @Override
  protected void doService(ToolPageContext page) throws IOException, ServletException {
    ToolUser user = page.getUser();
    Map<String, List<ToolUserDevice>> devicesByUserAgent =
        new CompactMap<String, List<ToolUserDevice>>();

    for (ToolUserDevice device :
        Query.from(ToolUserDevice.class).where("user = ?", user).selectAll()) {
      String userAgent = device.getUserAgentDisplay();
      List<ToolUserDevice> devices = devicesByUserAgent.get(userAgent);

      if (devices == null) {
        devices = new ArrayList<ToolUserDevice>();
        devicesByUserAgent.put(userAgent, devices);
      }

      devices.add(device);
    }

    final Map<ToolUserDevice, List<ToolUserAction>> actionsByDevice =
        new CompactMap<ToolUserDevice, List<ToolUserAction>>();

    for (Map.Entry<String, List<ToolUserDevice>> entry : devicesByUserAgent.entrySet()) {
      ToolUserDevice device = null;
      List<ToolUserAction> actions = null;
      long lastTime = 0;

      for (ToolUserDevice d : entry.getValue()) {
        List<ToolUserAction> a =
            Query.from(ToolUserAction.class)
                .where("device = ?", d)
                .sortDescending("time")
                .selectAll();

        if (!a.isEmpty()) {
          long time = a.get(0).getTime();

          if (lastTime < time) {
            lastTime = time;
            device = d;
            actions = a;
          }
        }
      }

      if (device != null) {
        actionsByDevice.put(device, actions);
      }
    }

    List<ToolUserDevice> recentDevices = new ArrayList<ToolUserDevice>(actionsByDevice.keySet());

    Collections.sort(
        recentDevices,
        new Comparator<ToolUserDevice>() {

          @Override
          public int compare(ToolUserDevice x, ToolUserDevice y) {
            long xTime = actionsByDevice.get(x).get(0).getTime();
            long yTime = actionsByDevice.get(y).get(0).getTime();

            return xTime < yTime ? 1 : (xTime > yTime ? -1 : 0);
          }
        });

    page.writeHeader();
    page.writeStart("div", "class", "widget", "style", "overflow: hidden;");
    page.writeStart("h1", "class", "icon icon-object-history");
    page.writeHtml(page.localize(ToolUserHistory.class, "title"));
    page.writeEnd();

    page.writeStart("div", "class", "tabbed");
    for (ToolUserDevice device : recentDevices) {
      List<ToolUserAction> actions = actionsByDevice.get(device);
      String lookingGlassUrl =
          page.cmsUrl("/lookingGlass", "id", device.getOrCreateLookingGlassId());

      page.writeStart("div", "data-tab", device.getUserAgentDisplay());
      page.writeStart(
          "div",
          "style",
          page.cssString(
              "float", "right",
              "text-align", "center"));
      page.writeStart(
          "a", "class", "icon icon-facetime-video", "target", "_blank", "href", lookingGlassUrl);
      page.writeHtml(page.localize(ToolUserHistory.class, "subtitle.lookingGlass"));
      page.writeEnd();

      page.writeElement("br");

      page.writeElement(
          "img",
          "width",
          150,
          "height",
          150,
          "src",
          page.cmsUrl(
              "qrCode",
              "data",
              JspUtils.getHostUrl(page.getRequest()) + lookingGlassUrl,
              "size",
              150));
      page.writeEnd();

      page.writeStart("ul", "class", "links", "style", page.cssString("margin-right", "150px"));
      for (ToolUserAction action : actions) {
        Object actionContent = action.getContent();

        if (actionContent == null) {
          continue;
        }

        page.writeStart("li");
        page.writeStart(
            "a", "target", "_top", "href", page.objectUrl("/content/edit.jsp", actionContent));
        page.writeTypeObjectLabel(actionContent);
        page.writeEnd();
        page.writeEnd();
      }
      page.writeEnd();
      page.writeEnd();
    }
    page.writeEnd();
    page.writeEnd();
    page.writeFooter();
  }
  @Override
  public void doService(ToolPageContext page) throws IOException, ServletException {
    ToolUser user = page.getUser();
    Dashboard dashboard = user.getDashboard();
    String dashboardId = "user";

    if (dashboard == null) {
      ToolRole role = user.getRole();

      if (role != null) {
        dashboard = role.getDashboard();
        dashboardId = "role";
      }
    }

    if (dashboard == null) {
      dashboard = page.getCmsTool().getDefaultDashboard();
      dashboardId = "tool";
    }

    if (dashboard == null) {
      dashboard = Dashboard.createDefaultDashboard();
      dashboardId = "default";
    }

    page.writeHeader();
    page.writeStart("div", "class", "dashboard-columns");
    List<DashboardColumn> columns = dashboard.getColumns();
    double totalWidth = 0;

    for (DashboardColumn column : columns) {
      double width = column.getWidth();
      totalWidth += width > 0 ? width : 1;
    }

    CmsTool cms = Query.from(CmsTool.class).first();
    Set<String> disabled = cms != null ? cms.getDisabledPlugins() : Collections.emptySet();

    for (int c = 0, cSize = columns.size(); c < cSize; ++c) {
      DashboardColumn column = columns.get(c);
      double width = column.getWidth();

      page.writeStart(
          "div",
          "class",
          "dashboard-column",
          "style",
          page.cssString("width", ((width > 0 ? width : 1) / totalWidth * 100) + "%"));

      List<DashboardWidget> widgets = column.getWidgets();

      for (int w = 0, wSize = widgets.size(); w < wSize; ++w) {
        DashboardWidget widget = widgets.get(w);

        if (disabled.contains(widget.getClass().getName())) {
          continue;
        }

        String widgetUrl =
            page.toolUrl(
                CmsTool.class,
                "/dashboardWidget/"
                    + dashboardId
                    + "/"
                    + widget.getClass().getName()
                    + "/"
                    + widget.getId());

        page.writeStart(
            "div", "class", "frame dashboard-widget", "data-dashboard-widget-url", widgetUrl);
        page.writeStart("a", "href", widgetUrl);
        page.writeEnd();
        page.writeEnd();
      }
      page.writeEnd();
    }
    page.writeEnd();
    page.writeFooter();
  }
  @Override
  protected void doService(ToolPageContext page) throws IOException, ServletException {
    ToolUser user = page.getUser();
    Collection<String> includeFields = Arrays.asList("returnToDashboardOnSave");
    Object object = Query.from(Object.class).where("_id = ?", page.param(UUID.class, "id")).first();
    State state = State.getInstance(object);
    ContentLock contentLock = null;

    if (object != null) {
      contentLock = ContentLock.Static.lock(object, null, user);
    }

    if (page.isFormPost()) {
      if (page.param(String.class, "action-edits") != null) {
        if (state != null) {
          Date newPublishDate = page.param(Date.class, "publishDate");

          if (newPublishDate != null) {
            Content.ObjectModification contentData = state.as(Content.ObjectModification.class);
            DateTimeZone timeZone = page.getUserDateTimeZone();
            newPublishDate =
                new Date(
                    DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
                        .withZone(timeZone)
                        .parseMillis(new DateTime(newPublishDate).toString("yyyy-MM-dd HH:mm:ss")));

            contentData.setPublishUser(page.getUser());
            contentData.setPublishDate(newPublishDate);
            state.save();
          }
        }

      } else if (page.param(String.class, "action-unlock") != null) {
        contentLock.delete();

        page.writeStart("script", "type", "text/javascript");
        page.writeRaw("window.location.reload();");
        page.writeEnd();

      } else if (page.param(String.class, "action-settings") != null) {
        try {
          page.include("/WEB-INF/objectPost.jsp", "object", user, "includeFields", includeFields);
          user.save();

        } catch (Exception error) {
          page.getErrors().add(error);
        }
      }
    }

    String returnUrl = page.param(String.class, "returnUrl");

    page.writeHeader();
    page.writeStart("style", "type", "text/css");
    page.writeCss(".cms-contentTools th", "width", "25%;");
    page.writeEnd();

    page.writeStart("div", "class", "widget cms-contentTools");
    page.writeStart("h1", "class", "icon icon-wrench");
    page.writeHtml("Tools");
    page.writeEnd();

    page.writeStart("div", "class", "tabbed");
    page.writeStart("div", "class", "fixedScrollable", "data-tab", "For Editors");
    if (object != null) {
      Content.ObjectModification contentData = state.as(Content.ObjectModification.class);
      Date publishDate = contentData.getPublishDate();
      ToolUser publishUser = contentData.getPublishUser();
      Date updateDate = contentData.getUpdateDate();
      ToolUser updateUser = contentData.getUpdateUser();

      page.writeStart("table", "class", "table-striped");
      page.writeStart("tbody");
      if (publishDate != null || publishUser != null) {
        page.writeStart("tr");
        page.writeStart("th");
        page.writeHtml("Published");
        page.writeEnd();

        page.writeStart("td");
        if (publishDate != null) {
          page.writeHtml(page.formatUserDateTime(publishDate));
        }
        page.writeEnd();

        page.writeStart("td");
        if (publishUser != null) {
          page.writeObjectLabel(publishUser);
        }
        page.writeEnd();
        page.writeEnd();
      }

      if (updateDate != null || updateUser != null) {
        page.writeStart("tr");
        page.writeStart("th");
        page.writeHtml("Last Updated");
        page.writeEnd();

        page.writeStart("td");
        if (updateDate != null) {
          page.writeHtml(page.formatUserDateTime(updateDate));
        }
        page.writeEnd();

        page.writeStart("td");
        if (updateUser != null) {
          page.writeObjectLabel(updateUser);
        }
        page.writeEnd();
        page.writeEnd();
      }
      page.writeEnd();
      page.writeEnd();

      page.writeStart("h2");
      page.writeHtml("Advanced Edits");
      page.writeEnd();

      if (page.isFormPost() && page.param(String.class, "action-edits") != null) {
        if (page.getErrors().isEmpty()) {
          page.writeStart("div", "class", "message message-success");
          page.writeHtml("Advanced edits successfully saved.");
          page.writeEnd();

        } else {
          page.include("/WEB-INF/errors.jsp");
        }
      }

      page.writeStart("form", "method", "post", "action", page.url(""));

      page.writeStart("div", "class", "inputContainer");
      page.writeStart("div", "class", "inputLabel");
      page.writeStart("label", "for", page.createId());
      page.writeHtml("New Publish Date");
      page.writeEnd();
      page.writeEnd();

      page.writeStart("div", "class", "inputSmall");
      page.writeElement(
          "input",
          "type",
          "text",
          "class",
          "date",
          "name",
          "publishDate",
          "value",
          page.formatUserDateTime(publishDate));
      page.writeEnd();
      page.writeEnd();

      page.writeStart("div", "class", "actions");
      page.writeStart(
          "button", "class", "icon icon-action-save", "name", "action-edits", "value", true);
      page.writeHtml("Save");
      page.writeEnd();
      page.writeEnd();
      page.writeEnd();

      if (!user.equals(contentLock.getOwner())) {
        page.writeStart("h2");
        page.writeHtml("Content Lock");
        page.writeEnd();

        page.writeStart("div", "class", "message message-warning");
        page.writeStart("p");
        page.writeHtml("Locked by ");
        page.writeObjectLabel(contentLock.getOwner());
        page.writeHtml(" since ");
        page.writeHtml(page.formatUserDateTime(contentLock.getCreateDate()));
        page.writeHtml(".");
        page.writeEnd();
        page.writeEnd();

        page.writeStart("form", "method", "post", "action", page.url(""));
        page.writeStart("div", "class", "actions");
        page.writeStart(
            "button", "class", "icon icon-unlock", "name", "action-unlock", "value", true);
        page.writeHtml("Unlock");
        page.writeEnd();
        page.writeEnd();
        page.writeEnd();
      }
    }

    page.writeStart("h2");
    page.writeHtml("Settings");
    page.writeEnd();

    if (page.isFormPost() && page.param(String.class, "action-settings") != null) {
      if (page.getErrors().isEmpty()) {
        page.writeStart("div", "class", "message message-success");
        page.writeHtml("Settings successfully saved.");
        page.writeEnd();

      } else {
        page.include("/WEB-INF/errors.jsp");
      }
    }

    page.writeStart("form", "method", "post", "action", page.url(""), "style", "margin-bottom:0;");
    page.include("/WEB-INF/objectForm.jsp", "object", user, "includeFields", includeFields);

    page.writeStart("div", "class", "actions");
    page.writeStart(
        "button", "class", "icon icon-action-save", "name", "action-settings", "value", true);
    page.writeHtml("Save");
    page.writeEnd();
    page.writeEnd();
    page.writeEnd();
    page.writeEnd();

    page.writeStart("div", "class", "fixedScrollable", "data-tab", "For Developers");
    page.writeStart("ul");
    if (object != null) {
      page.writeStart("li");
      page.writeStart("a", "target", "_blank", "href", page.objectUrl("/contentRaw", object));
      page.writeHtml("View Raw Data");
      page.writeEnd();
      page.writeEnd();
    }

    if (!ObjectUtils.isBlank(returnUrl)) {
      page.writeStart("li");
      if (ObjectUtils.to(
          boolean.class, StringUtils.getQueryParameterValue(returnUrl, "deprecated"))) {
        page.writeStart(
            "a",
            "target",
            "_top",
            "href",
            StringUtils.addQueryParameters(returnUrl, "deprecated", null));
        page.writeHtml("Hide Deprecated Fields");
        page.writeEnd();

      } else {
        page.writeStart(
            "a",
            "target",
            "_top",
            "href",
            StringUtils.addQueryParameters(returnUrl, "deprecated", true));
        page.writeHtml("Show Deprecated Fields");
        page.writeEnd();
      }
      page.writeEnd();
    }
    page.writeEnd();

    if (object != null) {
      ObjectType type = state.getType();

      page.writeStart("table", "class", "table-striped");
      page.writeStart("tbody");
      if (type != null) {
        Class<?> objectClass = type.getObjectClass();

        if (objectClass != null) {
          page.writeStart("tr");
          page.writeStart("th");
          page.writeStart("label", "for", page.createId());
          page.writeHtml("Class");
          page.writeEnd();
          page.writeEnd();

          page.writeStart("td");
          page.writeJavaClassLink(objectClass);
          page.writeEnd();
          page.writeEnd();
        }
      }

      page.writeStart("tr");
      page.writeStart("th");
      page.writeStart("label", "for", page.createId());
      page.writeHtml("ID");
      page.writeEnd();
      page.writeEnd();

      page.writeStart("td");
      page.writeElement(
          "input",
          "type",
          "text",
          "id",
          page.getId(),
          "class",
          "code",
          "value",
          state.getId(),
          "readonly",
          "readonly",
          "style",
          "width:100%;",
          "onclick",
          "this.select();");
      page.writeEnd();
      page.writeEnd();

      page.writeStart("tr");
      page.writeStart("th");
      page.writeStart("label", "for", page.createId());
      page.writeHtml("URL");
      page.writeEnd();
      page.writeEnd();

      page.writeStart("td");
      page.writeElement(
          "input",
          "type",
          "text",
          "id",
          page.getId(),
          "value",
          JspUtils.getAbsoluteUrl(
              page.getRequest(), page.cmsUrl("/content/edit.jsp", "id", state.getId())),
          "readonly",
          "readonly",
          "style",
          "width:100%;",
          "onclick",
          "this.select();");
      page.writeEnd();
      page.writeEnd();
      page.writeEnd();
      page.writeEnd();
    }

    if (object != null) {
      ObjectType type = state.getType();

      if (type != null) {
        if (!ObjectUtils.isBlank(type.as(Renderer.TypeModification.class).getEmbedPath())) {
          String permalink = state.as(Directory.ObjectModification.class).getPermalink();

          if (!ObjectUtils.isBlank(permalink)) {
            String siteUrl = Application.Static.getInstance(CmsTool.class).getDefaultSiteUrl();
            StringBuilder embedCode = new StringBuilder();

            embedCode.append("<script type=\"text/javascript\" src=\"");
            embedCode.append(
                StringUtils.addQueryParameters(
                    StringUtils.removeEnd(siteUrl, "/") + permalink,
                    "_embed",
                    true,
                    "_format",
                    "js"));
            embedCode.append("\"></script>");

            page.writeHtml("Embed Code:");
            page.writeElement("br");
            page.writeStart(
                "textarea",
                "class",
                "code",
                "data-expandable-class",
                "code",
                "readonly",
                "readonly",
                "onclick",
                "this.select();");
            page.writeHtml(embedCode);
            page.writeEnd();
          }
        }

        String defaultPath = type.as(Renderer.TypeModification.class).getPath();
        Map<String, String> paths = type.as(Renderer.TypeModification.class).getPaths();

        if (!ObjectUtils.isBlank(defaultPath) || !ObjectUtils.isBlank(paths)) {
          page.writeStart("h2");
          page.writeHtml("Renderers");
          page.writeEnd();

          page.writeStart("table", "class", "table-striped");
          page.writeStart("tbody");
          if (!ObjectUtils.isBlank(defaultPath)) {
            page.writeStart("tr");
            page.writeStart("th");
            page.writeStart("code");
            page.writeHtml("Default");
            page.writeEnd();
            page.writeEnd();

            page.writeStart("td");
            page.writeStart("code");
            page.writeStart(
                "a",
                "target",
                "_blank",
                "href",
                DebugFilter.Static.getServletPath(
                    page.getRequest(),
                    "code",
                    "action",
                    "edit",
                    "type",
                    "JSP",
                    "servletPath",
                    defaultPath));
            page.writeHtml(defaultPath);
            page.writeEnd();
            page.writeEnd();
            page.writeEnd();
            page.writeEnd();
          }

          for (Map.Entry<String, String> entry : paths.entrySet()) {
            page.writeStart("tr");
            page.writeStart("th");
            page.writeStart("code");
            page.writeHtml(entry.getKey());
            page.writeEnd();
            page.writeEnd();

            page.writeStart("td");
            page.writeStart("code");
            page.writeStart(
                "a",
                "target",
                "_blank",
                "href",
                DebugFilter.Static.getServletPath(
                    page.getRequest(),
                    "code",
                    "action",
                    "edit",
                    "type",
                    "JSP",
                    "servletPath",
                    entry.getValue()));
            page.writeHtml(entry.getValue());
            page.writeEnd();
            page.writeEnd();
            page.writeEnd();
            page.writeEnd();
          }
          page.writeEnd();
          page.writeEnd();
        }

        Class<?> objectClass = type.getObjectClass();

        if (objectClass != null) {
          Static.writeJavaAnnotationDescriptions(page, objectClass);
        }
      }
    }
    page.writeEnd();
    page.writeEnd();
    page.writeEnd();
    page.writeFooter();
  }