예제 #1
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(object model)
        {
            YTagBuilder tag = new YTagBuilder("input");

            FieldSetup(tag, FieldType.Normal);
            tag.MergeAttribute("name", FieldNamePrefix, true);
            tag.MergeAttribute("type", "hidden");
            tag.MergeAttribute("value", model == null ? "" : model.ToString());
            return(Task.FromResult(tag.ToString(YTagRenderMode.StartTag)));
        }
예제 #2
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public async Task <string> RenderAsync(Guid?model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            // dropdown
            List <SelectionItem <string> > list;

            list = (
                from p in await PageDefinition.GetDesignedPagesAsync() orderby p.Url select
                new SelectionItem <string> {
                Text = p.Url,
                Value = p.PageGuid.ToString(),
            }).ToList <SelectionItem <string> >();
            list.Insert(0, new SelectionItem <string> {
                Text = __ResStr("select", "(select)"), Value = null
            });

            string ddList = await DropDownListComponent.RenderDropDownListAsync(this, (model ?? Guid.Empty).ToString(), list, null);

            // link
            YTagBuilder tag = new YTagBuilder("a");

            PageDefinition page = null;

            if (model != null)
            {
                page = await PageDefinition.LoadAsync((Guid)model);
            }

            tag.MergeAttribute("href", (page != null ? page.EvaluatedCanonicalUrl : ""));
            tag.MergeAttribute("target", "_blank");
            tag.MergeAttribute("rel", "nofollow noopener noreferrer");
            tag.Attributes.Add(Basics.CssTooltip, __ResStr("linkTT", "Click to preview the page in a new window - not all pages can be displayed correctly and may require additional parameters"));

            tag.InnerHtml = tag.InnerHtml + ImageHTML.BuildKnownIcon("#PagePreview", sprites: Info.PredefSpriteIcons);
            string linkTag = tag.ToString(YTagRenderMode.Normal);

            hb.Append($@"
<div id='{DivId}' class='yt_pageselection t_edit'>
    <div class='t_select'>
        {ddList.ToString()}
    </div>
    <div class='t_link'>
        {linkTag}
    </div>
    <div class='t_description'>
    </div>
</div>");

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.PageSelectionEditComponent('{DivId}');");

            return(hb.ToString());
        }
예제 #3
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public async Task <string> RenderAsync(TimeOfDay model)
        {
            // we're reusing Time component
            await Manager.AddOnManager.AddTemplateFromUIHintAsync("Time");

            HtmlBuilder hb = new HtmlBuilder();

            hb.Append($"<div id='{ControlId}' class='yt_time t_edit'>");

            Dictionary <string, object> hiddenAttributes = new Dictionary <string, object>(HtmlAttributes)
            {
                { "__NoTemplate", true }
            };

            hb.Append(await HtmlHelper.ForEditComponentAsync(Container, PropertyName, null, "Hidden", HtmlAttributes: hiddenAttributes, Validation: Validation));

            YTagBuilder tag = new YTagBuilder("input");

            FieldSetup(tag, FieldType.Anonymous);
            tag.Attributes.Add("name", "dtpicker");

            if (model != null)
            {
                DateTime dt = model.AsDateTime();
                tag.MergeAttribute("value", Formatting.FormatTime(dt));
            }
            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            hb.Append($"</div>");

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.TimeEditComponent('{ControlId}');");

            return(hb.ToString());
        }
예제 #4
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(string model)
        {
            HtmlBuilder sb = new HtmlBuilder();

            YTagBuilder tagLabel = new YTagBuilder("label");

            FieldSetup(tagLabel, FieldType.Anonymous);
            if (string.IsNullOrEmpty(model)) // we're distinguishing between "" and " "
            {
                tagLabel.InnerHtml = "&nbsp;";
            }
            else
            {
                tagLabel.SetInnerText(model);
            }
            sb.Append(tagLabel.ToString(YTagRenderMode.Normal));

            string helpLink;

            if (TryGetSiblingProperty <string>($"{PropertyName}_HelpLink", out helpLink) && !string.IsNullOrWhiteSpace(helpLink))
            {
                YTagBuilder tagA = new YTagBuilder("a");
                tagA.Attributes.Add("href", Utility.UrlEncodePath(helpLink));
                tagA.Attributes.Add("target", "_blank");
                tagA.MergeAttribute("rel", "noopener noreferrer");
                tagA.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule("yt_extlabel_img"));
                tagA.InnerHtml = ImageHTML.BuildKnownIcon("#Help");
                sb.Append(tagA.ToString(YTagRenderMode.Normal));
            }

            return(Task.FromResult(sb.ToString()));
        }
예제 #5
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public async Task <string> RenderAsync(string model)
        {
            UseSuppliedIdAsControlId();

            HtmlBuilder hb = new HtmlBuilder();

            hb.Append($"<div id='{ControlId}' class='yt_ssn t_edit'>");

            Dictionary <string, object> hiddenAttributes = new Dictionary <string, object>(HtmlAttributes)
            {
                { "__NoTemplate", true }
            };

            hb.Append(await HtmlHelper.ForEditComponentAsync(Container, PropertyName, null, "Hidden", HtmlAttributes: hiddenAttributes, Validation: Validation));

            YTagBuilder tag = new YTagBuilder("input");

            FieldSetup(tag, FieldType.Anonymous);
            tag.Attributes.Add("name", "ssninput");

            if (model != null)
            {
                tag.MergeAttribute("value", model);
            }
            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            hb.Append($"</div>");

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.SSNEditComponent('{ControlId}');");

            return(hb.ToString());
        }
예제 #6
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(Decimal?model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            YTagBuilder tag = new YTagBuilder("input");

            tag.AddCssClass("yt_decimal");
            tag.AddCssClass("t_edit");
            FieldSetup(tag, Validation ? FieldType.Validated : FieldType.Normal);
            string id = MakeId(tag);

            // handle min/max
            float          min = 0, max = 99999999.99F;
            RangeAttribute rangeAttr = PropData.TryGetAttribute <RangeAttribute>();

            if (rangeAttr != null)
            {
                min = Convert.ToSingle(rangeAttr.Minimum);
                max = Convert.ToSingle(rangeAttr.Maximum);
            }
            string format = PropData.GetAdditionalAttributeValue("Format", "0.00");

            if (model != null)
            {
                tag.MergeAttribute("value", ((decimal)model).ToString(format));
            }

            hb.Append($@"
{tag.ToString(YTagRenderMode.StartTag)}");

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.DecimalEditComponent('{id}', {{ Min: {min}, Max: {max} }});");

            return(Task.FromResult(hb.ToString()));
        }
예제 #7
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(object model)
        {
            string text;

            if (model is MultiString)
            {
                text = (MultiString)model;
            }
            else
            {
                text = (string)model;
            }

            int emHeight = PropData.GetAdditionalAttributeValue("EmHeight", 10);

            HtmlBuilder hb = new HtmlBuilder();

            YTagBuilder tag = new YTagBuilder("textarea");

            tag.AddCssClass("yt_textareasourceonly");
            tag.AddCssClass("t_edit");
            tag.AddCssClass("k-textbox"); // USE KENDO style
            FieldSetup(tag, Validation ? FieldType.Validated : FieldType.Normal);
            tag.Attributes.Add("id", ControlId);
            tag.Attributes.Add("rows", emHeight.ToString());

            // handle StringLengthAttribute as maxlength
            StringLengthAttribute lenAttr = PropData.TryGetAttribute <StringLengthAttribute>();

            if (lenAttr != null)
            {
#if DEBUG
                if (tag.Attributes.ContainsKey("maxlength"))
                {
                    throw new InternalError($"Both StringLengthAttribute and maxlength specified - {FieldName}");
                }
#endif
                int maxLength = lenAttr.MaximumLength;
                if (maxLength > 0 && maxLength <= 8000)
                {
                    tag.MergeAttribute("maxlength", maxLength.ToString());
                }
            }
#if DEBUG
            if (lenAttr == null && !tag.Attributes.ContainsKey("maxlength"))
            {
                throw new InternalError($"No max string length given using StringLengthAttribute or maxlength - {FieldName}");
            }
#endif

            tag.SetInnerText(text);
            hb.Append(tag.ToString(YTagRenderMode.Normal));

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.TextAreaSourceOnlyEditComponent('{ControlId}');");

            return(Task.FromResult(hb.ToString()));
        }
예제 #8
0
        internal string GetModuleLink(Guid?model, bool force = false)
        {
            if (!force)
            {
                if (model == null || model == Guid.Empty)
                {
                    return("");
                }
            }
            YTagBuilder tag = new YTagBuilder("a");

            tag.MergeAttribute("href", ModuleDefinition.GetModulePermanentUrl(model ?? Guid.Empty));
            tag.MergeAttribute("target", "_blank");
            tag.MergeAttribute("rel", "nofollow noopener noreferrer");
            tag.Attributes.Add(Basics.CssTooltip, __ResStr("linkTT", "Click to preview the module in a new window - not all modules can be displayed correctly and may require additional parameters"));

            tag.InnerHtml = tag.InnerHtml + ImageHTML.BuildKnownIcon("#ModulePreview", sprites: Info.PredefSpriteIcons);
            return(tag.ToString(YTagRenderMode.Normal));
        }
예제 #9
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public async Task <string> RenderAsync(string model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            bool copy   = PropData.GetAdditionalAttributeValue <bool>("Copy", true);
            bool rdonly = PropData.GetAdditionalAttributeValue <bool>("ReadOnly", false);

            YTagBuilder tag = new YTagBuilder("input");

            tag.AddCssClass(TemplateClass);
            // adding k-textbox to the control makes it look like a kendo maskedtext box without the overhead of actually calling kendoMaskedTextBox
            tag.AddCssClass("k-textbox");
            tag.AddCssClass("t_display");
            tag.AddCssClass("k-state-disabled"); // USE KENDO style
            FieldSetup(tag, FieldType.Anonymous);

            tag.MergeAttribute("type", "text");
            tag.MergeAttribute("value", model ?? "");
            if (copy || rdonly)
            {
                tag.MergeAttribute("readonly", "readonly");
            }
            else
            {
                tag.MergeAttribute("disabled", "disabled");
            }

            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            if (copy)
            {
                await Manager.AddOnManager.AddAddOnNamedAsync(Package.AreaName, "clipboardjs.com.clipboard");// add clipboard support

                hb.Append(ImageHTML.BuildKnownIcon("#TextCopy", sprites: Info.PredefSpriteIcons, title: __ResStr("ttCopy", "Copy to Clipboard"), cssClass: "yt_text_copy"));
            }

            //Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.TextDisplayComponent('{ControlId}');");

            return(hb.ToString());
        }
예제 #10
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(object model)
        {
            YTagBuilder tag = new YTagBuilder("input");

            FieldSetup(tag, FieldType.Normal);
            tag.MergeAttribute("type", "hidden");
            if (HtmlAttributes.ContainsKey("--NoTemplate"))
            {
                HtmlAttributes.Remove("--NoTemplate");
            }
            else if (HtmlAttributes.ContainsKey("__NoTemplate"))
            {
                HtmlAttributes.Remove("__NoTemplate");
            }
            else
            {
                tag.AddCssClass("yt_hidden");
            }
            if (model != null && model.GetType().IsEnum)
            {
                model = (int)model;
            }
            tag.MergeAttribute("value", model == null ? "" : model.ToString());

            StringLengthAttribute lenAttr = PropData.TryGetAttribute <StringLengthAttribute>();

#if NOTYET
            if (lenAttr == null)
            {
                throw new InternalError($"No max string length given using StringLengthAttribute - {FieldName}");
            }
#endif
            if (lenAttr != null && lenAttr.MaximumLength > 0 && lenAttr.MaximumLength <= 8000)
            {
                tag.MergeAttribute("maxlength", lenAttr.MaximumLength.ToString());
            }

            return(Task.FromResult(tag.ToString(YTagRenderMode.StartTag)));
        }
예제 #11
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(string model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            int    tileSize = PropData.GetAdditionalAttributeValue("TileSize", 16);
            string palette  = PropData.GetAdditionalAttributeValue("Palette", "basic");
            int    columns  = PropData.GetAdditionalAttributeValue("Columns", 6);
            bool   preview  = PropData.GetAdditionalAttributeValue("Preview", true);

            YTagBuilder tag = new YTagBuilder("input");

            tag.AddCssClass("yt_colorpicker");
            tag.AddCssClass("t_edit");
            FieldSetup(tag, Validation ? FieldType.Validated : FieldType.Normal);
            tag.MergeAttribute("id", ControlId);
            if (model != null)
            {
                tag.MergeAttribute("value", model);
            }
            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            ScriptBuilder sb = new ScriptBuilder();

            sb.Append($@"(new YetaWF_ComponentsHTML.ColorPickerEditComponent('{ControlId}', {{
                palette: '{JE(palette)}',
                tileSize: {tileSize},
                preview: {JE(preview)},
                messages: {{
                    previewInput: '{JE(__ResStr("editColor", "Edit the color using hex values or names"))}',
                    cancel: '{JE(__ResStr("cancel", "Cancel"))}',
                    apply: '{JE(__ResStr("apply", "Apply"))}'
                }}
            }}));");

            Manager.ScriptManager.AddLast(sb.ToString());

            return(Task.FromResult(hb.ToString()));
        }
예제 #12
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(int?model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            YTagBuilder tag = new YTagBuilder("input");
            string      id  = MakeId(tag);

            tag.AddCssClass(TemplateClass);
            tag.AddCssClass("t_edit");
            tag.AddCssClass("yt_intvalue_base");
            FieldSetup(tag, Validation ? FieldType.Validated : FieldType.Normal);

            tag.MergeAttribute("maxlength", "20");

            if (model != null)
            {
                tag.MergeAttribute("value", ((int)model).ToString());
            }

            // handle min/max
            int            min = 0, max = 999999999;
            RangeAttribute rangeAttr = PropData.TryGetAttribute <RangeAttribute>();

            if (rangeAttr != null)
            {
                min = (int)rangeAttr.Minimum;
                max = (int)rangeAttr.Maximum;
            }
            string noEntry = PropData.GetAdditionalAttributeValue <string>("NoEntry", null);
            int    step    = PropData.GetAdditionalAttributeValue <int>("Step", 1);

            hb.Append($@"
{tag.ToString(YTagRenderMode.StartTag)}");

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.IntValueEditComponent('{id}', {{ Min: {min}, Max: {max}, Step: {step}, NoEntryText: '{JE(noEntry??"")}' }});");

            return(Task.FromResult(hb.ToString()));
        }
예제 #13
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public async Task <string> RenderAsync(DateTime?model)
        {
            UseSuppliedIdAsControlId();

            HtmlBuilder hb = new HtmlBuilder();

            hb.Append($"<div id='{ControlId}' class='yt_datetime t_edit'>");

            Dictionary <string, object> hiddenAttributes = new Dictionary <string, object>(HtmlAttributes)
            {
                { "__NoTemplate", true }
            };

            hb.Append(await HtmlHelper.ForEditComponentAsync(Container, PropertyName, null, "Hidden", HtmlAttributes: hiddenAttributes, Validation: Validation));

            YTagBuilder tag = new YTagBuilder("input");

            FieldSetup(tag, FieldType.Anonymous);
            tag.Attributes.Add("name", "dtpicker");

            // handle min/max date
            DateTimeSetup setup = new DateTimeSetup {
                Min = new DateTime(1900, 1, 1),
                Max = new DateTime(2199, 12, 31),
            };
            MinimumDateAttribute minAttr = PropData.TryGetAttribute <MinimumDateAttribute>();

            if (minAttr != null)
            {
                setup.Min = minAttr.MinDate;
            }
            MaximumDateAttribute maxAttr = PropData.TryGetAttribute <MaximumDateAttribute>();

            if (maxAttr != null)
            {
                setup.Max = maxAttr.MaxDate;
            }

            if (model != null)
            {
                tag.MergeAttribute("value", Formatting.FormatDateTime((DateTime)model));// shows date using user's timezone
            }
            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            hb.Append($"</div>");

            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.DateTimeEditComponent('{ControlId}', {Utility.JsonSerialize(setup)});");

            return(hb.ToString());
        }
예제 #14
0
        /// <summary>
        /// Adds HTML attributes and name= attribute to a tag.
        /// </summary>
        /// <param name="tag">The tag to which attributes are added.</param>
        /// <param name="fieldType">The type of the field.</param>
        /// <remarks>This is used for the main tag of a template.
        ///
        /// Also adds validation attributes depending on the field's type.</remarks>
        public void FieldSetup(YTagBuilder tag, FieldType fieldType)
        {
            if (HtmlAttributes != null)
            {
                tag.MergeAttributes(HtmlAttributes, false);
            }
            switch (fieldType)
            {
            case FieldType.Anonymous:
                break;

            case FieldType.Normal:
                tag.MergeAttribute("name", FieldName, false);
                break;

            case FieldType.Validated:
                tag.MergeAttribute("name", FieldName, false);
                // error state
                AddErrorClass(tag);
                // client side validation
                AddValidation(tag);
                break;
            }
        }
예제 #15
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(Decimal?model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            hb.Append($"<div id='{ControlId}' class='yt_currency t_edit y_inline'>");

            YTagBuilder tag = new YTagBuilder("input");

            FieldSetup(tag, Validation ? FieldType.Validated : FieldType.Normal);

            bool rdonly   = PropData.GetAdditionalAttributeValue <bool>("ReadOnly", false);
            bool disabled = PropData.GetAdditionalAttributeValue <bool>("Disabled", false);

            if (disabled)
            {
                tag.Attributes.Add("disabled", "disabled");
            }

            CurrencySetup setup = new CurrencySetup {
                Min      = 0,
                Max      = 999999999.99,
                ReadOnly = rdonly,
            };
            // handle min/max
            RangeAttribute rangeAttr = PropData.TryGetAttribute <RangeAttribute>();

            if (rangeAttr != null)
            {
                setup.Min = (double)rangeAttr.Minimum;
                setup.Max = (double)rangeAttr.Maximum;
            }
            if (model != null)
            {
                tag.MergeAttribute("value", Formatting.FormatAmount((decimal)model));
            }

            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            hb.Append($"</div>");
            Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.CurrencyEditComponent('{ControlId}', {Utility.JsonSerialize(setup)});");

            return(Task.FromResult(hb.ToString()));
        }
예제 #16
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(string model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            int tileSize = PropData.GetAdditionalAttributeValue("TileSize", 24);

            YTagBuilder tag = new YTagBuilder("div");

            tag.AddCssClass("yt_colorpicker");
            tag.AddCssClass("t_display");
            FieldSetup(tag, FieldType.Anonymous);
            string style = $"width:{tileSize}px;height:{tileSize}px";

            if (model != null)
            {
                style += $";background-color:{model}";
            }
            tag.MergeAttribute("style", style);
            hb.Append(tag.ToString(YTagRenderMode.Normal));

            return(Task.FromResult(hb.ToString()));
        }
예제 #17
0
        /// <summary>
        /// Returns the client-side validation message for a component with the specified field name.
        /// </summary>
        /// <param name="fieldName">The HTML field name.</param>
        /// <param name="htmlHelper">An instance of a YHtmlHelper.</param>
        /// <returns>Returns the client-side validation message for the component with the specified field name.</returns>
        public static string BuildValidationMessage(this YHtmlHelper htmlHelper, string fieldName)
        {
            var    modelState = htmlHelper.ModelState[fieldName];
            string error      = null;
            bool   hasError   = false;

            if (modelState == null)
            {
                // no errors
            }
            else
            {
                IEnumerable <string> errors = (from e in modelState.Errors select e.ErrorMessage);
                hasError = errors.Any();
                if (hasError)
                {
                    error = errors.First();
                }
            }

            YTagBuilder tagBuilder = new YTagBuilder("span");

            tagBuilder.MergeAttribute("data-v-for", fieldName);
            tagBuilder.AddCssClass(hasError ? "v-error" : "v-valid");

            if (hasError)
            {
                // we're building the same client side in validation.ts, make sure to keep in sync
                // <img src="${$YetaWF.htmlAttrEscape(YConfigs.Forms.CssWarningIconUrl)}" name=${name} class="${YConfigs.Forms.CssWarningIcon}" ${YConfigs.Basics.CssTooltip}="${$YetaWF.htmlAttrEscape(val.M)}"/>
                YTagBuilder tagImg = new YTagBuilder("img");
                tagImg.Attributes.Add("src", Forms.CssWarningIconUrl);
                tagImg.Attributes.Add("name", fieldName);
                tagImg.AddCssClass(Forms.CssWarningIcon);
                tagImg.Attributes.Add(Basics.CssTooltip, error);
                tagBuilder.InnerHtml = tagImg.ToString();
            }
            return(tagBuilder.ToString(YTagRenderMode.Normal));
        }
예제 #18
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(string model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            hb.Append("<div class='yt_image t_display'>");

            string imageType = PropData.GetAdditionalAttributeValue <string>("ImageType", null);
            int    width     = PropData.GetAdditionalAttributeValue("Width", 0);
            int    height    = PropData.GetAdditionalAttributeValue("Height", 0);

            if (string.IsNullOrWhiteSpace(imageType) && model != null && (model.IsAbsoluteUrl() || model.StartsWith("/") || model.StartsWith("data:")))
            {
                if (width != 0 || height != 0)
                {
                    throw new InternalError("Can't use Width or Height with external Urls");
                }

                YTagBuilder img = new YTagBuilder("img");
                img.Attributes.Add("src", model);
                if (!img.Attributes.ContainsKey("alt"))
                {
                    img.Attributes.Add("alt", __ResStr("altImage", "Image"));
                }
                hb.Append(img.ToString(YTagRenderMode.Normal));
            }
            else
            {
                if (string.IsNullOrWhiteSpace(imageType))
                {
                    throw new InternalError("No ImageType specified");
                }

                bool showMissing = PropData.GetAdditionalAttributeValue("ShowMissing", true);
                if (string.IsNullOrWhiteSpace(model) && !showMissing)
                {
                    return(Task.FromResult <string>(null));
                }

                string alt = null;
                if (HtmlAttributes.ContainsKey("alt"))
                {
                    alt = (string)HtmlAttributes["alt"];
                }
                string imgTag = ImageComponentBase.RenderImage(imageType, width, height, model, Alt: alt);

                bool linkToImage = PropData.GetAdditionalAttributeValue("LinkToImage", false);
                if (linkToImage)
                {
                    YTagBuilder link   = new YTagBuilder("a");
                    string      imgUrl = ImageHTML.FormatUrl(imageType, null, model);
                    link.MergeAttribute("href", imgUrl);
                    link.MergeAttribute("target", "_blank");
                    link.MergeAttribute("rel", "noopener noreferrer");
                    link.InnerHtml = imgTag;
                    hb.Append(link.ToString(YTagRenderMode.Normal));
                }
                else
                {
                    hb.Append(imgTag);
                }
            }

            hb.Append("</div>");
            return(Task.FromResult(hb.ToString()));
        }
예제 #19
0
        /// <summary>
        /// Renders a text input control.
        /// </summary>
        /// <param name="component">The current component being rendered.</param>
        /// <param name="model">The model.</param>
        /// <param name="templateCssClass">The CSS class to add to the template (starting with yt_).</param>
        /// <returns>The component rendered as HTML.</returns>
        public static async Task <string> RenderTextAsync(YetaWFComponent component, string model, string templateCssClass)
        {
            await IncludeExplicitAsync();

            HtmlBuilder hb = new HtmlBuilder();

            component.UseSuppliedIdAsControlId();

            YTagBuilder tag = new YTagBuilder("input");

            if (!string.IsNullOrWhiteSpace(templateCssClass))
            {
                tag.AddCssClass(templateCssClass);
            }
            tag.AddCssClass("yt_text_base");
            // adding k-textbox to the control makes it look like a kendo maskedtext box without the overhead of actually calling kendoMaskedTextBox
            tag.AddCssClass("k-textbox");
            tag.AddCssClass("t_edit");
            component.FieldSetup(tag, component.Validation ? FieldType.Validated : FieldType.Normal);
            tag.Attributes.Add("id", component.ControlId);
            //string id = null;
            //if (!string.IsNullOrWhiteSpace(mask)) {
            //    id = component.MakeId(tag);
            //}
            if (Manager.CurrentModule != null && Manager.CurrentModule.FormAutoComplete)
            {
                tag.MergeAttribute("autocomplete", "on", replaceExisting: false);
            }
            else
            {
                tag.MergeAttribute("autocomplete", "new-password", replaceExisting: false);
            }

            bool copy = component.PropData.GetAdditionalAttributeValue <bool>("Copy", false);
            //string mask = component.PropData.GetAdditionalAttributeValue<string>("Mask", null);

            // handle StringLengthAttribute as maxlength
            StringLengthAttribute lenAttr = component.PropData.TryGetAttribute <StringLengthAttribute>();

            if (lenAttr != null)
            {
#if DEBUG
                if (tag.Attributes.ContainsKey("maxlength"))
                {
                    throw new InternalError("Both StringLengthAttribute and maxlength specified - {0}", component.FieldName);
                }
#endif
                int maxLength = lenAttr.MaximumLength;
                if (maxLength > 0 && maxLength <= 8000)
                {
                    tag.MergeAttribute("maxlength", maxLength.ToString());
                }
            }
#if DEBUG
            if (lenAttr == null && !tag.Attributes.ContainsKey("maxlength"))
            {
                throw new InternalError("No max string length given using StringLengthAttribute or maxlength - {0}", component.FieldName);
            }
#endif
            // text
            tag.MergeAttribute("type", "text");
            tag.MergeAttribute("value", model ?? "");

            hb.Append($@"{tag.ToString(YTagRenderMode.StartTag)}");

            if (copy)
            {
                await Manager.AddOnManager.AddAddOnNamedAsync(component.Package.AreaName, "clipboardjs.com.clipboard");// add clipboard support

                hb.Append(ImageHTML.BuildKnownIcon("#TextCopy", sprites: Info.PredefSpriteIcons, title: __ResStr("ttCopy", "Copy to Clipboard"), cssClass: "yt_text_copy"));
            }

            //if (!string.IsNullOrWhiteSpace(mask)) {
            //    // if there is a Mask we need to use the KendoMaskedTextBox
            //    await KendoUICore.AddFileAsync("kendo.maskedtextbox.min.js");
            //    ScriptBuilder sb = new ScriptBuilder();
            //    sb.Append("$('#{0}').kendoMaskedTextBox({{ mask: '{1}' }});\n", id, Utility.JserEncode(mask));
            //    Manager.ScriptManager.AddLastDocumentReady(sb);
            //}
            //Manager.ScriptManager.AddLast($@"new YetaWF_ComponentsHTML.TextEditComponent('{component.ControlId}');");

            return(hb.ToString());
        }
예제 #20
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public Task <string> RenderAsync(string model)
        {
            if (string.IsNullOrWhiteSpace(model))
            {
                return(Task.FromResult <string>(null));
            }

            HtmlBuilder hb = new HtmlBuilder();

            hb.Append("<div class='yt_url t_display'>");

            string hrefUrl;

            if (!TryGetSiblingProperty($"{PropertyName}_Url", out hrefUrl))
            {
                hrefUrl = model;
            }

            if (string.IsNullOrWhiteSpace(hrefUrl))
            {
                // no link
                YTagBuilder tag = new YTagBuilder("span");
                FieldSetup(tag, FieldType.Anonymous);

                string cssClass = PropData.GetAdditionalAttributeValue("CssClass", "");
                if (!string.IsNullOrWhiteSpace(cssClass))
                {
                    tag.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule(cssClass));
                }

                tag.SetInnerText(model);
                hb.Append(tag.ToString(YTagRenderMode.Normal));
            }
            else
            {
                // link
                YTagBuilder tag = new YTagBuilder("a");
                FieldSetup(tag, FieldType.Anonymous);

                string cssClass = PropData.GetAdditionalAttributeValue("CssClass", "");
                if (!string.IsNullOrWhiteSpace(cssClass))
                {
                    tag.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule(cssClass));
                }

                tag.MergeAttribute("href", hrefUrl);
                tag.MergeAttribute("target", "_blank");
                tag.MergeAttribute("rel", "nofollow noopener noreferrer");
                string text;
                if (!TryGetSiblingProperty($"{PropertyName}_Text", out text))
                {
                    text = model;
                }
                tag.SetInnerText(text);
                string tooltip = null;
                TryGetSiblingProperty($"{PropertyName}_ToolTip", out tooltip);
                if (!string.IsNullOrWhiteSpace(tooltip))
                {
                    tag.MergeAttribute(Basics.CssTooltip, tooltip);
                }

                // image
                if (PropData.GetAdditionalAttributeValue("ShowImage", true))
                {
                    tag.InnerHtml = tag.InnerHtml + ImageHTML.BuildKnownIcon("#UrlRemote", sprites: Info.PredefSpriteIcons);
                }
                hb.Append(tag.ToString(YTagRenderMode.Normal));
            }
            hb.Append("</div>");
            return(Task.FromResult(hb.ToString()));
        }
예제 #21
0
        /// <summary>
        /// Called by the framework when the component needs to be rendered as HTML.
        /// </summary>
        /// <param name="model">The model being rendered by the component.</param>
        /// <returns>The component rendered as HTML.</returns>
        public async Task <string> RenderAsync(string model)
        {
            HtmlBuilder hb = new HtmlBuilder();

            UrlTypeEnum type = PropData.GetAdditionalAttributeValue("UrlType", UrlTypeEnum.Remote);

            UrlUI ui = new UrlUI {
                UrlType = type,
                _Local  = model,
                _Remote = model,
            };

            hb.Append($@"
<div id='{ControlId}' class='yt_url t_edit'>");

            YTagBuilder tag = new YTagBuilder("input");

            tag.AddCssClass("t_hidden");
            tag.Attributes["type"] = "hidden";
            FieldSetup(tag, FieldType.Validated);
            tag.MergeAttribute("value", model);
            hb.Append(tag.ToString(YTagRenderMode.StartTag));

            using (Manager.StartNestedComponent(FieldName)) {
                hb.Append($@"
    {await HtmlHelper.ForEditAsync(ui, nameof(ui.UrlType), Validation: false)}
");

                if ((type & UrlTypeEnum.Local) != 0)
                {
                    hb.Append($@"
    <div class='t_local'>
        {await HtmlHelper.ForEditAsync(ui, nameof(ui._Local), Validation: false)}
    </div>");
                }
                if ((type & UrlTypeEnum.Remote) != 0)
                {
                    hb.Append($@"
    <div class='t_remote'>
        {await HtmlHelper.ForEditAsync(ui, nameof(ui._Remote), Validation: false)}
    </div>");
                }
            }

            // link
            tag = new YTagBuilder("a");
            tag.MergeAttribute("href", Utility.UrlEncodePath(model));
            tag.MergeAttribute("target", "_blank");
            tag.MergeAttribute("rel", "nofollow noopener noreferrer");

            tag.InnerHtml = tag.InnerHtml + ImageHTML.BuildKnownIcon("#UrlRemote", sprites: Info.PredefSpriteIcons);
            string link = tag.ToString(YTagRenderMode.Normal);

            UrlEditSetup setup = new UrlEditSetup {
                Type = type,
                Url  = model,
            };

            hb.Append($@"
    <div class='t_link'>
        {link}
    </div>
</div>");

            Manager.ScriptManager.AddLast($"new YetaWF_ComponentsHTML.UrlEditComponent('{ControlId}', {Utility.JsonSerialize(setup)});");

            return(hb.ToString());
        }
예제 #22
0
        /// <summary>
        /// Renders the beginning &lt;form&gt; tag with the specified attributes.
        /// </summary>
        /// <param name="HtmlAttributes">The HTML attributes to add to the &lt;form&gt; tag.</param>
        /// <param name="SaveReturnUrl">Defines whether the return URL is saved when the form is submitted.</param>
        /// <param name="ValidateImmediately">Defines whether client-side validation is immediate (true) or delayed until form submission (false).</param>
        /// <param name="ActionName">Overrides the default action name.</param>
        /// <param name="ControllerName">Overrides the default controller name.</param>
        /// <param name="Pure">TODO: Purpose unclear.</param>
        /// <param name="Method">The method used to submit the form (get/post)</param>
        /// <returns>Returns the HTML with the generated &lt;form&gt; tag.</returns>
        protected async Task <string> RenderBeginFormAsync(object HtmlAttributes = null, bool SaveReturnUrl = false, bool ValidateImmediately = false, string ActionName = null, string ControllerName = null, string Method = "post")
        {
            await YetaWFCoreRendering.Render.AddFormsAddOnsAsync();

            await Manager.AddOnManager.AddAddOnNamedAsync("YetaWF_Core", "Forms");// standard css, validation strings

            await Manager.AddOnManager.AddAddOnNamedAsync("YetaWF_ComponentsHTML", "Forms");

            Manager.ScriptManager.AddLast("$YetaWF.Forms", "$YetaWF.Forms;");// need to evaluate for side effect to initialize forms

            Manager.NextUniqueIdPrefix();

            if (string.IsNullOrWhiteSpace(ActionName))
            {
                ActionName = GetViewName();
            }
            if (!ActionName.EndsWith(YetaWFViewExtender.PartialSuffix))
            {
                ActionName += YetaWFViewExtender.PartialSuffix;
            }
            if (string.IsNullOrWhiteSpace(ControllerName))
            {
                ControllerName = ModuleBase.Controller;
            }

            IDictionary <string, object> rvd = YHtmlHelper.AnonymousObjectToHtmlAttributes(HtmlAttributes);

            if (SaveReturnUrl)
            {
                rvd.Add(Basics.CssSaveReturnUrl, "");
            }

            string css = null;

            if (Manager.CurrentSite.FormErrorsImmed)
            {
                css = CssManager.CombineCss(css, "yValidateImmediately");
            }
            css = CssManager.CombineCss(css, Forms.CssFormAjax);
            rvd.Add("class", css);

            YTagBuilder tagBuilder = new YTagBuilder("form");

            tagBuilder.MergeAttributes(rvd, true);
            if (ModuleBase.FormAutoComplete)
            {
                tagBuilder.Attributes.Add("autocomplete", "on");
            }
            else
            {
                tagBuilder.Attributes.Add("autocomplete", "new-password");
            }

            string id = null;

            if (tagBuilder.Attributes.ContainsKey("id"))
            {
                id = (string)tagBuilder.Attributes["id"];
            }
            else
            {
                id = Manager.UniqueId();
                tagBuilder.Attributes.Add("id", id);
            }
            string formAction;

#if MVC6
            System.IServiceProvider services = HtmlHelper.ActionContext.HttpContext.RequestServices;
            IUrlHelper urlHelper             = services.GetRequiredService <IUrlHelperFactory>().GetUrlHelper(HtmlHelper.ActionContext);
            formAction = urlHelper.Action(action: ActionName, controller: ControllerName, new { area = HtmlHelper.RouteData.Values["area"] });
#else
            formAction = UrlHelper.GenerateUrl(null /* routeName */, ActionName, ControllerName, null, RouteTable.Routes, HtmlHelper.RequestContext, true /* includeImplicitMvcValues */);
#endif
            tagBuilder.MergeAttribute("action", formAction, true);
            tagBuilder.MergeAttribute("method", Method, true);

            // show errors if already present
            if (!HtmlHelper.ModelState.IsValid)
            {
                Manager.ScriptManager.AddLast($@"
var f = $YetaWF.getElementById('{id}');
if ($YetaWF.Forms.hasErrors(f))
    $YetaWF.Forms.showErrors(f);
");
            }

            return(tagBuilder.ToString(YTagRenderMode.StartTag));
        }
예제 #23
0
        internal static async Task <string> RenderActionAsync(ModuleAction action, ModuleAction.RenderModeEnum mode, string id,
                                                              ModuleAction.RenderEngineEnum RenderEngine = ModuleAction.RenderEngineEnum.KendoMenu, int BootstrapSmartMenuLevel = 0, bool HasSubmenu = false)
        {
            // check if we're in the right mode
            if (!await action.RendersSomethingAsync())
            {
                return(null);
            }

            await Manager.AddOnManager.AddTemplateFromUIHintAsync("ActionIcons");// this is needed because we're not used by templates

            if (!string.IsNullOrWhiteSpace(action.ConfirmationText) && (action.Style != ModuleAction.ActionStyleEnum.Post && action.Style != ModuleAction.ActionStyleEnum.Nothing))
            {
                throw new InternalError("When using ConfirmationText, the Style property must be set to Post");
            }
            if (!string.IsNullOrWhiteSpace(action.PleaseWaitText) && (action.Style != ModuleAction.ActionStyleEnum.Normal && action.Style != ModuleAction.ActionStyleEnum.Post))
            {
                throw new InternalError("When using PleaseWaitText, the Style property must be set to Normal or Post");
            }
            if (action.CookieAsDoneSignal && action.Style != ModuleAction.ActionStyleEnum.Normal)
            {
                throw new InternalError("When using CookieAsDoneSignal, the Style property must be set to Normal");
            }

            ModuleAction.ActionStyleEnum style = action.Style;
            if (style == ModuleAction.ActionStyleEnum.OuterWindow)
            {
                if (!Manager.IsInPopup)
                {
                    style = ModuleAction.ActionStyleEnum.Normal;
                }
            }

            if (style == ModuleAction.ActionStyleEnum.Popup || style == ModuleAction.ActionStyleEnum.PopupEdit)
            {
                if (Manager.IsInPopup)
                {
                    style = ModuleAction.ActionStyleEnum.NewWindow;
                }
            }

            if (style == ModuleAction.ActionStyleEnum.Popup || style == ModuleAction.ActionStyleEnum.PopupEdit || style == ModuleAction.ActionStyleEnum.ForcePopup)
            {
                await YetaWFCoreRendering.Render.AddPopupsAddOnsAsync();
            }

            bool newWindow = false, outerWindow = false;
            bool popup = false, popupEdit = false;
            bool nothing = false, post = false;

            switch (style)
            {
            default:
            case ModuleAction.ActionStyleEnum.Normal:
                break;

            case ModuleAction.ActionStyleEnum.NewWindow:
                newWindow = true;
                break;

            case ModuleAction.ActionStyleEnum.Popup:
            case ModuleAction.ActionStyleEnum.ForcePopup:
                popup = Manager.CurrentSite.AllowPopups;
                break;

            case ModuleAction.ActionStyleEnum.PopupEdit:
                popup     = Manager.CurrentSite.AllowPopups;
                popupEdit = Manager.CurrentSite.AllowPopups;
                break;

            case ModuleAction.ActionStyleEnum.OuterWindow:
                outerWindow = true;
                break;

            case ModuleAction.ActionStyleEnum.Nothing:
                nothing = true;
                break;

            case ModuleAction.ActionStyleEnum.Post:
                post = true;
                break;
            }

            YTagBuilder tag = new YTagBuilder("a");

            if (!string.IsNullOrWhiteSpace(action.Tooltip))
            {
                tag.MergeAttribute(Basics.CssTooltip, action.Tooltip);
            }
            if (!string.IsNullOrWhiteSpace(action.Name))
            {
                tag.MergeAttribute("data-name", action.Name);
            }
            if (!action.Displayed)
            {
                tag.MergeAttribute("style", "display:none");
            }
            if (HasSubmenu)
            {
                if (RenderEngine == ModuleAction.RenderEngineEnum.BootstrapSmartMenu)
                {
                    tag.AddCssClass("dropdown-toggle");
                    tag.Attributes.Add("data-toggle", "dropdown-toggle");
                }
                tag.Attributes.Add("aria-haspopup", "true");
                tag.Attributes.Add("aria-expanded", "false");
            }
            if (RenderEngine == ModuleAction.RenderEngineEnum.BootstrapSmartMenu)
            {
                tag.AddCssClass(BootstrapSmartMenuLevel <= 1 ? "nav-link" : "dropdown-item");
            }

            if (!string.IsNullOrWhiteSpace(id))
            {
                tag.Attributes.Add("id", id);
            }

            if (!string.IsNullOrWhiteSpace(action.CssClass))
            {
                tag.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule(action.CssClass));
            }
            string extraClass;

            switch (mode)
            {
            default:
            case ModuleAction.RenderModeEnum.Button: extraClass = "y_act_button"; break;

            case ModuleAction.RenderModeEnum.ButtonIcon: extraClass = "y_act_buttonicon"; break;

            case ModuleAction.RenderModeEnum.ButtonOnly: extraClass = "y_act_buttononly"; break;

            case ModuleAction.RenderModeEnum.IconsOnly: extraClass = "y_act_icon"; break;

            case ModuleAction.RenderModeEnum.LinksOnly: extraClass = "y_act_link"; break;

            case ModuleAction.RenderModeEnum.NormalLinks: extraClass = "y_act_normlink"; break;

            case ModuleAction.RenderModeEnum.NormalMenu: extraClass = "y_act_normmenu"; break;
            }
            tag.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule(extraClass));

            string url = action.GetCompleteUrl(OnPage: true);

            if (!string.IsNullOrWhiteSpace(url))
            {
                tag.MergeAttribute("href", Utility.UrlEncodePath(url));
                if (Manager.CurrentPage != null)
                {
                    string currUrl = Manager.CurrentPage.EvaluatedCanonicalUrl;
                    if (!string.IsNullOrWhiteSpace(currUrl) && currUrl != "/")  // this doesn't work on home page because everything matches
                    {
                        if (action.Url == currUrl)
                        {
                            tag.AddCssClass("t_currenturl");
                        }
                        if (currUrl.StartsWith(action.Url))
                        {
                            tag.AddCssClass("t_currenturlpart");
                        }
                    }
                }
            }
            else
            {
                tag.MergeAttribute("href", "javascript:void(0);");
            }

            if (!string.IsNullOrWhiteSpace(action.ConfirmationText))
            {
                if (action.Category == ModuleAction.ActionCategoryEnum.Delete)
                {
                    // confirm deletions?
                    if (UserSettings.GetProperty <bool>("ConfirmDelete"))
                    {
                        tag.MergeAttribute(Basics.CssConfirm, action.ConfirmationText);
                    }
                }
                else
                {
                    // confirm actions?
                    if (UserSettings.GetProperty <bool>("ConfirmActions"))
                    {
                        tag.MergeAttribute(Basics.CssConfirm, action.ConfirmationText);
                    }
                }
            }
            if (!string.IsNullOrWhiteSpace(action.PleaseWaitText))
            {
                tag.MergeAttribute(Basics.CssPleaseWait, action.PleaseWaitText);
            }
            if (action.CookieAsDoneSignal)
            {
                tag.Attributes.Add(Basics.CookieDoneCssAttr, "");
            }
            if (action.SaveReturnUrl)
            {
                tag.Attributes.Add(Basics.CssSaveReturnUrl, "");
                if (!action.AddToOriginList)
                {
                    tag.Attributes.Add(Basics.CssDontAddToOriginList, "");
                }
            }
            if (!string.IsNullOrWhiteSpace(action.ExtraData))
            {
                tag.Attributes.Add(Basics.CssExtraData, action.ExtraData);
            }
            if (action.NeedsModuleContext)
            {
                tag.Attributes.Add(Basics.CssAddModuleContext, "");
            }

            if (post)
            {
                tag.Attributes.Add(Basics.PostAttr, "");
            }
            if (action.DontFollow || action.CookieAsDoneSignal || post || nothing)
            {
                tag.MergeAttribute("rel", "nofollow"); // this is so bots don't follow this assuming it's a simple page (Post actions can't be retrieved with GET/HEAD anyway)
            }
            if (outerWindow)
            {
                tag.Attributes.Add(Basics.CssOuterWindow, "");
            }
            if (!nothing)
            {
                tag.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule(Basics.CssActionLink));
            }
            if (newWindow)
            {
                tag.MergeAttribute("target", "_blank");
                tag.MergeAttribute("rel", "noopener noreferrer");
            }
            if (popup)
            {
                tag.AddCssClass(Manager.AddOnManager.CheckInvokedCssModule(Basics.CssPopupLink));
                if (popupEdit)
                {
                    tag.Attributes.Add(Basics.CssAttrDataSpecialEdit, "");
                }
            }
            if (mode == ModuleAction.RenderModeEnum.Button || mode == ModuleAction.RenderModeEnum.ButtonIcon || mode == ModuleAction.RenderModeEnum.ButtonOnly)
            {
                tag.Attributes.Add(Basics.CssAttrActionButton, "");
            }

            bool   hasText = false, hasImg = false;
            string innerHtml = "";

            if (mode != ModuleAction.RenderModeEnum.LinksOnly && mode != ModuleAction.RenderModeEnum.ButtonOnly && !string.IsNullOrWhiteSpace(action.ImageUrlFinal))
            {
                string text = mode == ModuleAction.RenderModeEnum.NormalMenu ? action.MenuText : action.LinkText;
                if (RenderEngine == ModuleAction.RenderEngineEnum.KendoMenu)
                {
                    innerHtml += ImageHTML.BuildKnownIcon(action.ImageUrlFinal, alt: text, cssClass: Basics.CssNoTooltip + " k-image"); // k-image is needed to align <i> and <img> correctly
                }
                else
                {
                    innerHtml += ImageHTML.BuildKnownIcon(action.ImageUrlFinal, alt: text, cssClass: Basics.CssNoTooltip);
                }
                hasImg = true;
            }
            if (mode != ModuleAction.RenderModeEnum.IconsOnly && mode != ModuleAction.RenderModeEnum.ButtonIcon)
            {
                string text = mode == ModuleAction.RenderModeEnum.NormalMenu ? action.MenuText : action.LinkText;
                if (!string.IsNullOrWhiteSpace(text))
                {
                    innerHtml += Utility.HtmlEncode(text);
                    hasText    = true;
                }
            }
            if (hasText)
            {
                if (hasImg)
                {
                    tag.AddCssClass("y_act_textimg");
                }
                else
                {
                    tag.AddCssClass("y_act_text");
                }
            }
            else
            {
                if (hasImg)
                {
                    tag.AddCssClass("y_act_img");
                }
            }
            if (HasSubmenu && RenderEngine == ModuleAction.RenderEngineEnum.BootstrapSmartMenu)
            {
                innerHtml += " <span class='caret'></span>";
            }

            tag.AddCssClass(Globals.CssModuleNoPrint);
            tag.InnerHtml = innerHtml;

            return(tag.ToString(YTagRenderMode.Normal));
        }