One of the functionalities in my current flex application requires me to maintain a comments section. Here, the users can post the comments and the replies to existing comments. All I want is like the usual thread style commenting.
Let say, I am replying to someone else's comment, so, it will align my comment by a tab or so and people can easily see the comments and replies.
e.g.
USER ABC : COMMENT 1
-----USER XYZ: RE:COMMENT1
----------USER DEF: RE:RE:COMMENT1
and so on...
Can anyone suggest a way to do this?
Thanks :)
In your database you would store the parentID for each comment. You can query that data and loop through it creating new objects to display the comments however you want.
Store the results in an ArrayCollection
var tmpCanvas:Canvas;
for(var i:int = 0; i < ac.length; i++)
{
// display data
tmpCanvas = new Canvas();
tmpCanvas.x = ...
tmpCanvas.y = ...
// add items to the canvas
this.addChild(tmpCanvas);
}
Related
I am currently coding a plugin for ILIAS. The plugin itself is not at all complex but it contains several issues whereas I think we could make it simpler as it is.
The situation is following: We have a global advanced meta data field added in the user defined meta data section with a bijective identifier. The field is activated at a repository objected named course. We have manipulated the GUI with the plugin based on ilUIHookPluginGUI.
The code for this is ... well ... see it for yourself.
First of all we save the ID of the new meta data field in the settings at the ConfigGUI for the plugin:
$field_settings = new ilSetting("foo");
$field_id_value = $field_settings->set("field_id",$_POST["field_id"]);
In our class which extends ilUIHookPluginGUI we are loading the setting as following and we have the ID of the field:
$field_settings = new ilSetting("foo");
$field_id_value = $field_settings->get("field_id");
Now the fun part. With this ID and the ref_id of the object (well, we also load the object to get the ObjId) we can load the value of the meta data field setted at the course:
$object = \ilObjectFactory::getInstanceByRefId($_GET[ 'ref_id' ]);
$obj_id = $object->getId();
$result = $DIC->database()->query("SELECT value FROM adv_md_values_text WHERE obj_id = '".$obj_id."' AND field_id = '".$field_id_value."'");
$value = $DIC->database()->fetchAssoc($result);
$is_active = $value['value'];
The question is ... is there an easier way to achieve my result?
Best,
Laura
Nice question. First of all, note that I consider the advanced metadata service in ILIAS to be lacking a good readme making clear, which hooks the interface is offering for tasks such as yours. Some time ago, I had to deal with this service as well and run into similar issues. Hopefully, your question helps to document this a little better an I myself am looking forward to other suggestions, knowing that mine is not really good as well. If you have any resources, helping pushing the introduction of good readme for services and also pushing services towards using the repository pattern with a clear interface would be highly appreciated.
Concering your question of what can be improved: I see three main issues in the lines of code:
Storing an ID in the config of your plugin. Your plugin will unconfigurable for non-technical people. However, also for you this will be error prone, think about exporting-importing stuff from a test-installation to production.
Access the value by query instead of the service.
Using new and static functions inside your code making it untestable.
Step 1
Lets start with the first one. Note, that I did not manage to solve this one without introducing a new one (a new query). Bad I know. I hope that there is a better solution, I did not find one after quick research. You store the id, since the field title is not securely unique, right? This is correct, however, you could think about storing the tripplet of field_title, record_title and (maybe) scope. Note that you maybe do not need the scope since you want to use this globally. A function return you and array containing field_id and record_id could look like so:
function getFieldAndRecordIdByFieldTitles($field_title, $record_title, $scope_title){
$query = "select field.field_id,field.record_id from adv_mdf_definition as field
INNER JOIN adv_md_record as record ON record.record_id = field.record_id
INNER JOIN adv_md_record_scope as scope ON scope.record_id = field.record_id
INNER JOIN object_reference as ref ON scope.ref_id = ref.ref_id
INNER JOIN object_data as scope_data ON ref.obj_id = scope_data.obj_id
WHERE field.title='$field_title' AND record.title='$record_title' AND scope_data.title = '$scope_title'";
$set = $this->dic()->database()->query($query);
if($row = $this->dic()->database()->fetchAssoc($set))
{
return array_values($row);
}
}
Then get your values like so:
list($field_id,$record_id) = getFieldAndRecordIdByFieldTitles("my_field", "my_record", "my_scope");
Note that I am aware that I am introducing a new query here. Sorry, was the best I could come up with. I am sure there you find a better solution, if your research a bit, let us know if successful. However, we will remove one in the next step.
Step 2
Use the undocumented service, the get your value out of the advance meta data. Since you now have the record id and the field id, you can to that like so:
$record_values = new ilAdvancedMDValues($record_id, $obj_id);
$record_values->read();
$ADTGroup = $ilAdvancedMDValues->getADTGroup();
$ADT = $ilADTGroup->getElement($field_id);
$value = $ADT->getText();
/**if you have text, others are possible, such as:
switch (true) {
case ($ADT instanceof ilADTText):
break;
case ($ADT instanceof ilADTDate):
$value = $ADT->getDate();
break;
case ($ADT instanceof ilADTExternalLink):
$... = $ADT->getUrl();
$... = $ADT->getTitle();
break;
case ($ADT instanceof ilADTInternalLink):
$... = $ADT->setTargetRefId($value);
}
**/
Note that ADT's are also undocumented. There might be a better way, to get a value out of this.
Step 3
Wrap your statics and new into some injectable dependency. I usually use the bloated constructor pattern to do this. Looks like so:
public function __construct(InjectedSettings $mySettings = null)
{
if (!$mySettings) //Case in the default scenario
{
$this->mySettings = new InjectedSettings();
} else //used e.g. for unit tests, where you can stuff the constructor with a mock
{
$this->mySettings = $mySettings;
}
$this->mySettings->doSometing();
}
Note that this is not real dep. injection, still you still use new, but I think a very workable fix to use dep. injection at least for the test context in ilias.
Does this help? I hope there will be other (better answers as well).
I am building an application that will have the ability to create agenda items to discuss in a meeting. The agenda item might include one or more attachments to discuss so there is a one to many relation between the AgendaItems and the AgendaDocs models. So far, I have an insert form that looks like this:
The "Select File" button is a drive picker and the code I have inside the onDocumentSelect event is the following:
var docs = result.docs;
var createDataSource = app.datasources.AgendaDocs.modes.create;
for(var i=0; i<docs.length-1; i++){
var uniqueDraft = createDataSource.item;
createDataSource.items.push(uniqueDraft);
}
for(var i=0; i<createDataSource.items.length-1; i++){
var draft = createDataSource.item;
createDataSource.items[i].DocTitle = docs[i].name;
createDataSource.items[i].DocURL = docs[i].url;
createDataSource.items[i].DriveID = docs[i].id;
}
console.log(createDataSource.items);
The code is supposed to fill out the the List widget below the "Select File" button, but as how you see, the three items are the same. The datasource of the List widget is "AgendaDocs.modes.create" and the datasource of the insert form is "AgendaItems.modes.create".
Reading the official documentation from appmaker, makes me think it is possible since the properties of "CreateDataSource" includes "items". I need help from an expert here. Is this possible? Am I using the wrong approach?
First things first, it seems that you are trying to create records from different models and relationship between them in a one call... at this time App Maker is not that smart to digest such a complex meal. Most likely you'll need to break your flow into multiple steps:
Create (persist) Agenda Item
Create AgendaDocs records and relation with AgendaItem
Similar flow is implemented in Travel Approval template app, but it is not exactly the same as yours, since it doesn't create associations in batches.
Going back to the original question. Yep, it is possible to have multiple drafts, but not with the Create Datasource. You are looking for Manual Save Mode. Somewhere in perfect world your code would look similar to this:
// AgendaItems in Manual Save mode
var agendaDs = app.datasources.AgendaItems;
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it.
agendaDs.createItem();
var agendaDraft = agendaDs.item;
// Field values can be populated from UI via bindings...
agendaDraft.Type = 'X';
agendaDraft.Description = 'Y';
// onDocumentSelect Drive Picker's event handler
var docsDs = agendaDs.relations.AgendaDocs;
result.docs.forEach(function(doc) {
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it...however it will throw an exception
// with this message:
// Cannot save a foreign key association for the 'AgendaItem'
// relation because the target record has not been persisted
// to the server. To fix this, call saveChanges()
// on the data source for that record's model: AgendaItem
docsDs.createItem();
var docDraft = docsDs.item;
docDraft.DocTitle = doc.name;
docDraft.DocURL = doc.url;
docDraft.DriveID = doc.id;
});
// submit button click
agendaDraft.saveChanges();
Is it possible to programatically sort properties on DocumentType? I am creating them from code, but not sure how to order them.
Any suggestions much appreciated.
It depends on how and what you want to sort, but here's an example of how you might sort them:
DocumentType dt = DocumentType.GetByAlias("umbTextpage");
//Get the one you want to move to the top.
var property = dt.PropertyTypes.First(p => p.Alias == "bodyText");
//Get the rest. Make sure you have the right TabId.
var otherProperties = dt.PropertyTypes.Where(p => p.Alias != "bodyText" && p.TabId == 8).ToList();
property.SortOrder = 0;
property.Save();
int i = 1;
foreach (var p in otherProperties)
{
p.SortOrder = i++;
p.Save();
}
Hopefully, that gives you some sort of starting place, if you haven't already figured it our already... The easiest way to get the TabId is to look at the cmsTab table in the database.
Do you refer sorting based of the sub-pages of particular node? I mean your question little confuse...
anyway if you want to sort sub-pages; you can do that by something like following.
var eventsNode = #Model.NodeById(1058).orderbydesending("createddate");
Thanks,
Developerjigar
I'm totally new to Flex (moving away from Java for now :().
I have an XML file, I read the data and want to add it to a List and then use the List to be displayed.
Something like this is what I want to do:
List data = new ArrayList();
data.add(item1);
data.add(item2);
How can I do this, also how will I display the List in a table!!
Your question is rather... vague.
In Flex there are multiple ways to read data from XML and display it.
One approach would be using a HttpService to read the XML.
Then you can loop through the data manually or serve it as a DataProvider for a DataGrid.
I suggest you take a look at the Flex documentation, flexexamples.com and the flex quickstart. Topics that could help you:
http://www.adobe.com/devnet/flex/quickstart/httpservice/
http://www.adobe.com/devnet/flex/quickstart/using_data_providers/
http://blog.flexexamples.com/2007/07/26/displaying-xml-data-in-a-datagrid/
Nerver mind folks, I figured it out myself :)
var hostList : Array=[];
var i: int = numberOfHosts;
var hostNames: XMLList = hostPointXMLList.elements("HostName");
for (i=0; i< hostNames.length(); i++) {
//Alert.show("num="+i+" Hostname="+hostNames[i]);
hostList[i] = hostNames[i];
}
I have overcome a problem, but my solution seems really inefficient and clunky. Please consider this problem:
I have an array collection with articles in.
This array collection is filtered to a currently selected article category.
There is a cursor bound to a view that shows the current article.
When a category is deleted I need to delete orphened articles, I can't use the cursor or collection without affecting the views as they are bound.
I can itterate over the source, but if I delete (splice) from the source I have to break and start again, as all the articles indexes change, even when using for each.
This seems like a really inefficient way of doing things, and I'm sure there is a better way to do it, eithe by using another itterator on the same source, or unbinding the views unill I have finished updating, etc
Please let me know if I am missing a trick as I'm sure this is a really common problem/issue.
Thanks
Rob
p.s. Wrote this on my iPhone. Sorry for any mistakes!
Run the loop backwards.
So, instead of, say:
var len:int = arr.length;
for(var i:int = 0; i < len; i++) {
if(some condition) {
arr.splice(i,1);
}
}
do this:
for(var i:int = arr.length - 1; i >= 0; i--) {
if(some condition) {
arr.splice(i,1);
}
}
Simplest solution would probably be to just save the indexes you need to remove in a temporary array. Then after you've iterated through the collection, go back and just remove the items in the temporary array.
But from what I can gather, you should probably use a hash (Object) or something instead of an array structure.