Customize html output for a field's droplist options - html

I have a field called icon, which is a droplist sourced from folder in the content tree. I would like the list to not just show the text value(shown in the screen shot) but also to utilize an icon font and display what the actual icon would look like. Basically customizing the content editor's droplist for this field from:
<option value="gears">gears</option>
to
<option value="gears">gears <span class="my-icon-font-gears"></span></option>
Is there any documentation on how to modify the outputted html for a droplist, and to modify the content editor page to load another link, in this case a font-file.

I created a module on the marketplace that does something similar. You can have a look here. There is some documentation on there explaining how to use it.
The code is also on Git if you want to have a look.

Suggest you use the Droplink field type instead of the Droplist, since the value is stored by GUID and this will lead to less longer term problems if the link item is renamed or moved. In any case you need a custom field, inherit from Sitecore.Shell.Applications.ContentEditor.LookupEx (which is the DropLink field type) and override the DoRender() method with the custom markup you require.
It's not possible to embed a span tag since the option tag cannot contain other tags as it is invalid HTML. Adding it will cause the browser to strip it out. You can however set the class on the option itself and style that.
`<option value="gears" style="my-icon-font-gears">gears</option>`
Here is some sample code to achieve the field.
using System;
using System.Web.UI;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
namespace MyProject.CMS.Custom.Controls
{
public class StyledLookupEx : Sitecore.Shell.Applications.ContentEditor.LookupEx
{
private string _styleClassField;
private string StyleClassField
{
get
{
if (String.IsNullOrEmpty(_styleClassField))
_styleClassField = StringUtil.ExtractParameter("StyleClassField", this.Source).Trim();
return _styleClassField;
}
}
// This method is copied pasted from the base class apart from thhe single lined marked below
protected override void DoRender(HtmlTextWriter output)
{
Assert.ArgumentNotNull(output, "output");
Item[] items = this.GetItems(Sitecore.Context.ContentDatabase.GetItem(this.ItemID, Language.Parse(this.ItemLanguage)));
output.Write("<select" + this.GetControlAttributes() + ">");
output.Write("<option value=\"\"></option>");
bool flag1 = false;
foreach (Item obj in items)
{
string itemHeader = this.GetItemHeader(obj);
bool flag2 = this.IsSelected(obj);
if (flag2)
flag1 = true;
/* Option markup modified with class added */
output.Write("<option value=\"" + this.GetItemValue(obj) + "\"" + (flag2 ? " selected=\"selected\"" : string.Empty) + " class=\"" + obj[StyleClassField] + "\" >" + itemHeader + "</option>");
}
bool flag3 = !string.IsNullOrEmpty(this.Value) && !flag1;
if (flag3)
{
output.Write("<optgroup label=\"" + Translate.Text("Value not in the selection list.") + "\">");
output.Write("<option value=\"" + this.Value + "\" selected=\"selected\">" + this.Value + "</option>");
output.Write("</optgroup>");
}
output.Write("</select>");
if (!flag3)
return;
output.Write("<div style=\"color:#999999;padding:2px 0px 0px 0px\">{0}</div>", Translate.Text("The field contains a value that is not in the selection list."));
}
}
}
This field adds a custom properties to allow you to specify the linked field to use for the style class. The assumption is that you have another single line text field on the linked item to specify the CSS class.
Usage: Set the source property of the field in the following format:
Datasource={path-or-guid-to-options}&StyleClassField={fieldname}
e.g. Datasource=/sitecore/content/lookup/iconfonts&StyleClassField=IconClassName
To use this new field compile the above code in to project, switch over to the core database and then create a new field type – you can duplicate the existing Droplink field located in /sitecore/system/Field types/Link Types/Droplink. Delete the existing Control field and instead set the ASSEMBLY and CLASS fields to point to your implementation.
You also need to load a custom CSS stylesheet with the style defintions into the Content Editor, which you can achieve that by following this blog post.

Related

How to fix function declaration formatting in PhpStorm (2020.1.2) if signature longer then defined row length?

For example I have function declaration like this:
public function someLongMethodWithLongParamName(int $longparamName): VeryLongReturnValueType {}
with row length limit that less than chars count of function signature.
If I type Ctrl + Alt + L PhpStorm will format this row, but in strange way:
public function someLongMethodWithLongParamName(int $longparamName
): VeryLongReturnValueType {
}
(PhpStorm left parameter in the line of method name). If I will add one more parameter, PhpStorm will format line correct:
public function someLongMethodWithLongParamName(
int $longparamName,
bool $flag
): VeryLongReturnValueType {
}
Maybe someone deal with such bug?
P.S. Here are my Code Style settings:
Looks like you want it to wrap parameter & its type also? Then remove the Place ')' on new line checkbox in Method declaration parameters settings from your screenshot.

How to add Multiple CSS classes Using Bindngs

I have a data binding, In which I have to apply two css classes
data-bind ="css: isHiddenStage"
isHiddenStage ==> function returning a css class based on some Logic,
This is working fine, and I want to apply another css classes based on some condition
css:{ my-class:$index() + 1 === 10 }
Note: Here i can't use isHiddenStage function to check the condition
So finally I got this:
data-bind ="css: isHiddenStage, css:{ my-class:$index() + 1 === 10 }"
Which Is not working may be because, I can't use css twice in a binding.
Is there any alternative.
Regards
There cannot be multiple css bindings on the same element. Create a function that returns all css classes separated by spaces to be used in a single css binding.
HTML
<div data-bind="css: getCssClassesForIndex($index)"></div>
View Model
this.getCssClassesForIndex = function (index) {
var cssClasses = this.isHiddenStage() || '';
if ((index + 1) === 10) {
cssClasses += ' my-class';
}
return cssClasses;
}.bind(this);
What about something like:
data-bind ="css:{ isHiddenStage: true, 'my-class': $index() + 1 === 10 }"
This way the isHiddenStage() class will always be applied since its condition is always true.
Note that I put 'my-class' in quotes because it was not a valid identifier.
Knockout documentation link source

Loop Through HTML Elements and Nodes

I'm working on an HTML page highlighter project but ran into problems when a search term is a name of an HTML tag metadata or a class/ID name; eg if search terms are "media OR class OR content" then my find and replace would do this:
<link href="/css/DocHighlighter.css" <span style='background-color:yellow;font-weight:bold;'>media</span>="all" rel="stylesheet" type="text/css">
<div <span style='background-color:yellow;font-weight:bold;'>class</span>="container">
I'm using Lucene for highlighting and my current code (sort of):
InputStreamReader xmlReader = new INputStreamReader(xmlConn.getInputStream(), "UTF-8");
if (searchTerms!=null && searchTerms!="") {
QueryScorer qryScore = new QueryScorer(qp.parse(searchTerms));
Highlighter hl = new Highlighter(new SimpleHTMLFormatter(hlStart, hlEnd), qryScore);
}
if (xmlReader!=null) {
BufferedReader br = new BufferedReader(xmlReader);
String inputLine;
while((inputLine = br.readLine())!=null) {
String tmp = inputLine.trim();
StringReader strReader = new stringReader(tmp);
HTMLStripCharFilter htm = HTMLStripCharFilter(strReader.markSupported() ? strReader : new BufferedReader(strReader));
String tHL = hl.getBestFragment(analyzer, "", htm);
tmp = (tHL==null ? tmp : tHL);
}
xmlDoc+=tmp;
}
bufferedReader.close()
As you can see (if you understand Lucene highlighting) this does an indiscriminate find/replace. Since my document will be HTML and the search terms are dictated by users there is no way for me to parse on certain elements or tags. Also, since the find/replace basically loops and appends the HTML to a string (the return type of the method) I have to keep all HTML tags and values in place and order. I've tried using Jsoup to loop through the page but handles the HTML tag as one big result. I also tried tag soup to remove the broken HTML caused by the problem but it doesn't work correctly. Does anyone know how to basically loop though the elements and node (data value) of html?
I've been having the most luck with this
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" enconding=\"UTF-8\"?><!DOCTYPE html>");
Document doc = Jsoup.parse(txt.getResult());
Element elements = doc.getAllElements();
for (Element e : elements) {
if (!(e.tagName().equalsIgnoreCase("#root"))) {
sb.append("<" + e.tagName() + e.attributes() + ">" + e.ownText() + "\n");
}// end if
}// end for
return sb;
The one snag I still get is the nesting isn't always "repaired" properly but still semi close. I'm working more on this.

How to provide multiple links in one cell of a QTableView

I have a QTableView in my project, in which several columns display data that includes a hyperlink. I use a delegate class for these to set it up so that when the cell in the column is clicked, it opens the linked page in the browser. This works great... when it's only one value being linked to one page. For example, I may have a list of search values for mysite.com where the columns have values A, B, C, etc.. If the user clicks on the cell in this column with A, it will open a hyperlink for mysite.com/A (again, this part works fine). However, I now need to add a column that may have something like "A,B", where it needs to support links to search for A AND B in the same cell depending on which they click. I've been searching around online for a while now and it seems like this probably can't be done with a delegate. I have a line in a QTextBrowser elsewhere in my code where I can do this via HTML, like this:
QString toShow;
for(int i = 0; i < searchValueList.size(); i++)
{
toShow.append("`<a href=\"www.mysite.com/" + searchValueList.at(i) + "\"`>" +
searchValueList.at(i) + "`</a`>";
}
However I can't find any way to set the cells in a QTableView to recognize HTML formatting or Rich Text, and alas I'm not even sure that's possible. Is there any way at all to do what I'm trying to accomplish?
You can create a custom QItemDelegate for the specific column in which you can display rich text. The delegate could be like :
class RichTextDelegate: public QItemDelegate
{
public:
RichTextDelegate(QObject *parent = 0);
void paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
};
RichTextDelegate::RichTextDelegate(QObject *parent):QItemDelegate(parent)
{
}
void RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if( option.state & QStyle::State_Selected )
painter->fillRect( option.rect, option.palette.highlight() );
painter->save();
QTextDocument document;
document.setTextWidth(option.rect.width());
QVariant value = index.data(Qt::DisplayRole);
if (value.isValid() && !value.isNull())
{
document.setHtml(value.toString());
painter->translate(option.rect.topLeft());
document.drawContents(painter);
}
painter->restore();
}
You should set the item delegate for the specific column :
ui->tableView->setItemDelegateForColumn(colIndex, new RichTextDelegate(ui->tableView));
Now if you set the model text for the specific column in a row to a rich text, it will be shown properly :
model->item(rowIndex, colIndex)->setText(someRichText);

tt_news - where is the register "newsMoreLink" be defined?

The extension tt_news is very useful for me but there is this little thingy called "register:newsMoreLink". This register does contain the singlePid of the contentelement (defined a single view page) and the uid of the newsarticle from the news extension.
This is the typoscript section of the "new ts" of the extension tt_news
As you can see there is "append.data = register:newsMoreLink"...
plugin.tt_news {
displayLatest {
subheader_stdWrap {
# the "more" link is directly appended to the subheader
append = TEXT
append.data = register:newsMoreLink
append.wrap = <span class="news-list-morelink">|</span>
# display the "more" link only if the field bodytext contains something
append.if.isTrue.field = bodytext
outerWrap = <p>|</p>
}
}
}
What is "register:newsMoreLink"? Is this like a function or something? I do not know. But "register:newsMoreLink" produces a strange link if I use this on "append.data". It produces are "More >" link. The "More >" link after a news article teaser looks like this:
http://192.168.1.29/website/index.php?id=474&tx_ttnews%5Btt_news%5D=24&cHash=95d80a09fb9cbade7e934cda5e14e00a
474 is the "singlePid" (this is what it calls in the database
24 is the "uid" of the news article (the ones you create with the tt_news plugin in the backend)
My question is: Where is the "register:newsMoreLink" defined? Is it defined generally or do I miss a fact of Typo3..? How can I add an anchor link at the end of this "More >" href? Like:
http://192.168.1.29/website/index.php?id=474&tx_ttnews%5Btt_news%5D=24&cHash=95d80a09fb9cbade7e934cda5e14e00a#myAnchor1
register:newsMoreLink is not a function. It's one of the data types. In other words a type of data that you can access with stdWrap.data. register is set with LOAD_REGISTER. Though, in case of tt_news this is set in the PHP code with $this->local_cObj->LOAD_REGISTER().
I'm afraid you cannot easily add an anchor to that link. However, you can set the append to create your own custom link to the news record using typolink:
append = TEXT
append {
value = text of the link
typolink {
# ...typolink configuration...
}
}
You shall be interested in the typolink's attributes parameter, additionalParams and section.
this is the code I use to link to an pid with a anchor target:
displayList.plugin.tt_news.subheader_stdWrap {
append = TEXT
append.data >
append {
value = mehr
typolink{
parameter = 47 // pid
section = entry_{field:uid} // anchor name
section.insertData = 1
}
}