CKEditor Link dialog modification - modal-dialog

I am trying to add a drop down to CKEditor's link dialog. When selected, the drop down should insert corresponding class name to the link.
CKEDITOR.on( 'dialogDefinition', function( ev ) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if ( dialogName == 'link' ) {
var infoTab = dialogDefinition.getContents( 'info' );
infoTab.add({
type: 'select',
label: 'Display link as a button',
id: 'buttonType',
'default': '',
items: [
['- Not button -', ''],
['Button one', 'btn-primary'],
['Button two', 'btn-success'],
['Button three', 'btn-danger']
],
commit: function(data) {
data.className = this.getValue();
}
});
}
});
I have a feeling commit function is not doing the job, but cannot figure out how to make it work. I saw a code that almost does the same thing as I want at http://www.lxg.de/code/simplify-ckeditors-link-dialog. I tried it and it does not work either.
I am using CKEditor 4.3.2.
I appreciate your help in advance.

If you console.log the data object in link dialog's onOk, you'll find quite a different hierarchy. Element classes are in data.advanced.advCSSClasses. But even if you decide to override (or extend) the value of this property in your commit, your string will be nuked by the original commit of advCSSClasses input field ("Advanced" tab) anyway. So the approach got to be a little bit different:
Always store the value of the select in data.
Override commit of advCSSClasses input field to consider stored value.
Remember to execute the original commit of advCSSClasses input.
Here we go:
CKEDITOR.on( 'dialogDefinition', function( ev ) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if ( dialogName == 'link' ) {
var infoTab = dialogDefinition.getContents( 'info' ),
advTab = dialogDefinition.getContents( 'advanced' ),
advCSSClasses = advTab.get( 'advCSSClasses' );
infoTab.add( {
type: 'select',
label: 'Display link as a button',
id: 'buttonType',
'default': '',
items: [
['- Not button -', ''],
['Button one', 'btn-primary'],
['Button two', 'btn-success'],
['Button three', 'btn-danger']
],
commit: function( data ) {
data.buttonType = this.getValue();
}
});
var orgAdvCSSClassesCommit = advCSSClasses.commit;
advCSSClasses.commit = function( data ) {
orgAdvCSSClassesCommit.apply( this, arguments );
if ( data.buttonType && data.advanced.advCSSClasses.indexOf( data.buttonType ) == -1 )
data.advanced.advCSSClasses += ' ' + data.buttonType;
};
}
});
Now you got to only write a setup function which will detect whether one of your button classes is present to set a proper value of your select field once the dialog is open.

Related

TYPO3 I got an error by list after the installation of my own plugin

My TYPO3 version is 10.4.21 and I use extensions (Fluid styled content, sitepackage, Bootstrap, pizpalue and my own plugin which I made).
I created a plugin which names New Flipbox, because I have to make a new content element with a function of flipbox for my project work. But I have an error.
If I click a list button on the menu on the left side, an error occurred:
Oops, an error occurred! An exception occurred while executing 'SELECT
uid FROM tx_myextensionkey_domain_model_newflipbox WHERE
(tx_myextensionkey_domain_model_newflipbox.pid = ?) AND
((tx_myextensionkey_domain_model_newflipbox.deleted = 0) AND
((tx_myextensionkey_domain_model_newflipbox.t3ver_wsid = 0) AND
(tx_myextensionkey_domain_model_newflipbox.t3ver_oid = 0)))
LIMIT 1' with params [1]: SQLSTATE[42S02]: Base table or view not
found: 1146 Table
'd037b84f.tx_rsnmizukiflipbox_domain_model_newflipbox' doesn't exist
How can I fix it?
For now, I've written down these:
in /typo3conf/ext/myextension/Configuration/TCA/Overrides/tt_content.php:
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(
array(
'My Flipbox',
'rsnflipbox',
'EXT:core/Resources/Public/Icons/T3Icons/content/content-carousel-image.svg'
),
'CType',
'myextensionkey'
);
// Configure the default backend fields for the content element
$GLOBALS['TCA']['tt_content']['types']['flipbox_contentelement'] = [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
header; Internal title (not displayed),
bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
--palette--;;access,
',
'columnsOverrides' => [
'bodytext' => [
'config' => [
'enableRichtext' => true,
'richtextConfiguration' => 'default',
],
],
],
];
in /typo3conf/ext/myextension/ext_location.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig(
'mod {
wizards.newContentElement.wizardItems.plugins {
elements {
newflipbox {
iconIdentifier = content-dashboard
title = LLL:EXT:myextensionkey/Resources/Private/Language/locallang_db.xlf:tx_myextensionkey_newflipbox.name
description = LLL:EXT:myextensionkey/Resources/Private/Language/locallang_db.xlf:tx_myextensionkey_newflipbox.description
tt_content_defValues {
CType = list
list_type = key_newflipbox
}
}
}
show = *
}
}'
);
// wizards bei Plugin
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig(
'mod {
wizards.newContentElement.wizardItems.interactive {
elements {
newflipbox {
iconIdentifier = content-dashboard
title = LLL:EXT:myextensionkey/Resources/Private/Language/locallang_db.xlf:tx_myextensionkey_newflipbox.name
description = LLL:EXT:myextensionkey/Resources/Private/Language/locallang_db.xlf:tx_myextensionkey_newflipbox.description
tt_content_defValues {
CType = list
list_type = key_newflipbox
}
}
}
show = *
}
}'
);
$iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
$iconRegistry->registerIcon(
'myextensionkey-plugin-newflipbox',
\TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class,
['source' => 'EXT:myextensionkey/Resources/Public/Icons/user_plugin_newflipbox.svg']
);
});
in /typo3conf/ext/myextension/Configuration/TypoScript/setup.typoscript
lib.contentElement {
templateRootPaths.200 = EXT:myextensionkey/Resources/Private/Templates/
}
lib.contentElement {
partialRootPaths.200 = EXT:myextensionkey/Resources/Private/Partials/
}
lib.contentElement {
layoutRootPaths.200 = EXT:myextensionkey/Resources/Private/Layouts/
}
tt_content {
flipbox_contentelement =< lib.contentElement
flipbox_contentelement {
templateName = NewContentElement
}
}
I saw a website of TYPO3(https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/ContentElements/AddingYourOwnContentElements.html) to create a element content, but I can't go on because of some errors.
What should I do now? I hope someone can help me.
Thank you.
The error message clearly states that the table tx_rsnmizukiflipbox_domain_model_newflipbox is missing. So you should check if you have added ext_tables.sql with the necessary database fields and then you should go to the install tool and do a database compare to create the missing table.

ReactDataGrid row selection does not work

I am trying to build data table using react and react-data-grid version "^7.0.0-canary.16",
The render method looks like this:
render() {
return (
<div className={"component"}>
<ReactDataGrid width={600} height={400}
rowKey="id"
columns={this.state.columns}
rows={this.state.rows}
onRowClick={this.onRowClick}
rowSelection={{
showCheckbox: true,
enableShiftSelect: true,
onRowsSelected: this.onRowsSelected,
onRowsDeselected: this.onRowsDeselected,
selectBy: {
indexes: this.state.selectedIndexes
}
}}
/>
</div>
)
}
So following the documentation on page https://adazzle.github.io/react-data-grid/docs/examples/row-selection
it should display checkbox in first column and when I select the checkbox it should call method this.onRowsSelected.
Alas, no checkbox is shown and no matter how I click the this.onRowsSelected method is never called.
On the other hand the method this.onRowClick is called, whenever I click somewhere in the table.
Does anyone have experience with this?
It seems to be showing the checkboxes with "react-data-grid": "6.1.0"
Although, I'm having issue with the checkboxes when we filter the data. The rowIdx changes and we lose context of that was previously selected. We want to make BE calls on selected Data. I tried changing it to use the row.id but no luck. It messes up the selection.
Here is a hook for managing the selection
import {useState} from 'react';
export const useRowSelection = () => {
const [selectedIndexes, setSelectedIndexes] = useState([]);
const onRowsSelected = rows => {
setSelectedIndexes(prevState => {
return prevState.concat(rows.map(r => r.rowIdx));
});
};
const onRowsDeselected = rows => {
let rowIndexes = rows.map(r => r.rowIdx);
setSelectedIndexes(prevState => {
return prevState.filter(i => rowIndexes.indexOf(i) === -1);
});
};
return {
selectedIndexes,
onRowsDeselected,
onRowsSelected,
};
};
Pass them to the RDG
const {selectedIndexes, onRowsDeselected, onRowsSelected} = useRowSelection();
const rowSelectionProps = enableRowSelection
? {
showCheckbox: true,
enableShiftSelect: true,
onRowsSelected: onRowsSelected,
onRowsDeselected: onRowsDeselected,
selectBy: {
indexes: selectedIndexes,
},
}
: undefined;
<ReactDataGrid
columns={columnDefinition}
getValidFilterValues={getFilterValues}
rowGetter={i => filteredData[i]}
rowsCount={filteredData.length}
onAddFilter={filter => handleOnAddFilter(filter)}
onClearFilters={() => handleOnCleanFilters()}
toolbar={toolbar}
contextMenu={contextMenu}
RowsContainer={ContextMenuTrigger}
rowSelection={rowSelectionProps}
rowKey="id"
/>

Conditionally populating a path in a Mongo query

I have the following schema:
const connectionSchema = new Schema( {
...
event: {
type: Schema.ObjectId,
ref: 'events'
},
place: {
type: Schema.ObjectId,
ref: 'places'
},
} )
const eventsSchema = new Schema( {
...
place: {
type: Schema.ObjectId,
ref: 'places'
},
} )
When I query for either a single connection or a collection of connections, I want it to check if event is specified and if so return the place from the event, otherwise return the place from the connection.
I'm currently doing something like this when I'm querying a single connection:
let c
const execFunction = ( err, connection ) => {
if ( connection.event ) {
connection.place = connection.event.place
}
c = connection
}
const connectionPromise = Connection.findById( connectionId )
connectionPromise.populate( { path: 'event', populate: { path: 'place' } } )
connectionPromise.populate( 'place' )
connectionPromise.exec( execFunction )
But, I'm trying to avoid having to iterate through the entire collection in the execFunction to do the replace logic on each individual result if I query for a collection instead.
Is there a better (i.e. more performant) way to approach this?
As far as I know, there is no way to do conditional population with mongoose.
In your case, I think you are trying to do "deep population".
Deep population is not supported out of the box, but there is a plugin for that! mongoose-deep-populate
const connectionQuery = Connection.findById( connectionId )
connectionQuery.populate( 'place event event.place' )
connectionQuery.exec( execFunction );
I'm guessing that 'place event.place' will probably do it: 'event' should be implied.
I also expect that, if place or event or event.place are undefined, there won't be an error, they simply won't be populated.
But I don't think there is any way, in one line, to skip populating place when event.place is available.
By the way, if you want to use promises, you could write like this:
return Connection.findById( connectionId )
.populate( 'place event event.place' )
.then( onSuccessFunc );
to propagate errors, or:
Connection.findById( connectionId )
.populate( 'place event event.place' )
.then( onSuccessFunc )
.catch( onErrorFunc );
to handle errors locally. I find that preferable to callbacks, because we don't have to worry about if (err) at every stage.

How to disable unavailable dates in the CJuiDatePicker widget

I am building a scheduling app. I am able to sort the database using the CJuiDatePicker widget's "onSelect" option. Now I am trying to use the "beforeShow" option to ensure that only dates that have tasks can be selectable. Can anyone help?
$this->widget('zii.widgets.jui.CJuiDatePicker', array(
'model'=>$dataProvider,
'attribute'=>'start_time',
'options'=>array(
'dateFormat' => 'yy-mm-dd',
'changeMonth' => true,
'changeYear' => true,
'beforeShowDay' => 'js:$.datepicker.noWeekends',
'beforeShow' => "js:function Check_Alert2(){
Check_Alert();
}
",
'onSelect' => "js:function SearchFunc() {
var data = $('input').serialize();
var url = document.URL;
var params = $.param(data);
url = url.substr(0, url.indexOf('?'));
window.History.pushState(null, document.title,$.param.querystring(url, data));
}"
),
'htmlOptions'=>array(
'style'=>'height:20px;',
'readonly' =>true,
),
));
i am still using the original code. I need an array generated within the beforeshowday. When i run your code it fails on the getdates() function call.
'beforeShowDay' => 'js:function(date){
var array = ["2013-10-30","2013-10-31","2013-12-18","2013-12-25"];
var string = jQuery.datepicker.formatDate("yy-mm-dd", date);
if (array.indexOf(string) == -1){
return [false,"", "No event"];
} else return [true,"", "Event"];

cakePHP + extjs row editor and REST

I've implemented REST routing in cakePHP to properly route REST style requests to the proper methods in my controller.
This is what I've added to my routes.php
Router::mapResources(array('object_fields'));
This properly routes the REST requests to my index/add/edit/delete methods inside my controller.
In my EXTJS grid I am using the row editor with a restful store to achieve CRUD behavior.
Here is the code for my grid
myapp.object_field_grid = Ext.extend(Ext.grid.GridPanel, {
closable: true,
stripeRows: true,
frame: true,
viewConfig: {
forceFit: true
},
editor: new Ext.ux.grid.RowEditor({
saveText: 'Update',
}),
onAdd : function(btn, ev){
var u = new this.store.recordType({
name : '',
type: '',
});
this.editor.stopEditing();
this.store.insert(0, u);
this.editor.startEditing(0);
},
onDelete : function(){
},
initComponent: function() {
var proxy = new Ext.data.HttpProxy({
url: 'object_fields/',
});
var reader = new Ext.data.JsonReader({
totalProperty: 'totalCount',
successProperty: 'success',
idProperty: 'id',
root: 'data',
messageProperty: 'message'
}, [
{name: 'id'},
{name: 'name', allowBlank: false},
{name: 'type', allowBlank: false},
]);
var writer = new Ext.data.JsonWriter({
encode: false,
});
var store = new Ext.data.Store({
baseParams: {id: this.object_id},
id: 'object_fields',
restful: true,
proxy: proxy,
reader: reader,
writer: writer,
});
store.load();
var object_field_columns = [
// {header: "id", width: 250, sortable: true, dataIndex: 'id', editor: new Ext.form.TextField({})},
{header: "name", width: 250, sortable: true, dataIndex: 'name', editor: new Ext.form.TextField({})},
{header: "type", width: 250, sortable: true, dataIndex: 'type', editor: new Ext.form.ComboBox({editable: false, store:['STRING', 'NUMBER']})},
];
var config = {
columns: object_field_columns,
store: store,
plugins: [this.editor],
//autoHeight: true,
height: 200,
tbar: [{
text: 'Add',
iconCls: 'silk-add',
handler: this.onAdd,
scope: this,
}, '-', {
text: 'Delete',
iconCls: 'silk-delete',
handler: this.onDelete,
scope: this,
}, '-'],
}
Ext.apply(this, Ext.apply(this.initialConfig, config));
myapp.object_field_grid.superclass.initComponent.apply(this, arguments);
},
onRender: function() {
this.store.load();
myapp.object_field_grid.superclass.onRender.apply(this, arguments);
}
});
Ext.reg('object_field_grid', myapp.object_field_grid); // register xtype
My GET/POST requests are being properly routed to my index/add methods inside my controller and I am able to easily retrieve the paramaters that I pass it in the request.
My problem is with the update functionality PUT request. The PUT request does get successfully routed to my edit method inside the controller.
This is what the request looks like in firebug
http://server.local/object_fields/20
JSON
data
Object { name="test7777777777", more...}
id
"18"
Source
{"id":"18","data":{"name":"test7777777777","id":"20"}}
Inside my edit method I'm not receiving my array that I passed through the PUT request.
When I dump $this->params inside my edit method this is what is in the array.
([id] => 20
[named] => Array
(
)
[pass] => Array
(
[0] => 20
)
[controller] => object_fields
[action] => edit
[[method]] => PUT
[plugin] =>
[url] => Array
(
[ext] => html
[url] => object_fields/20
)
[form] => Array
(
)
[isAjax] => 1
)
How can I properly receive my array through the PUT request inside my edit method?
UPDATE:
I am able to retrieve my array using the following code inside the edit method
function edit($id){
$this->autoRender = false;
echo 'edit';
$raw = '';
$httpContent = fopen('php://input', 'r');
while ($kb = fread($httpContent, 1024)) {
$raw .= $kb;
}
fclose($httpContent);
$params = array();
parse_str($raw, $params);
print_r($params);
}
The question is now why does cakePHP not do this automaticly?
put this in your app_controller.php:
public function beforeFilter() {
if (
isset($this->RequestHandler) &&
$this->RequestHandler->requestedWith('json') &&
(
$this->RequestHandler->isPost() ||
$this->RequestHandler->isPut()
)
) {
$jsonData = json_decode(utf8_encode(trim(file_get_contents('php://input'))), true);
if (is_array($jsonData)) {
$this->data = $jsonData;
unset($jsonData);
}
}
}