How to create an Observable of cursor - sqlite

I have cursor returned by an SQLite query, I would like to know correct approach for creating an Observable the emits each row in the cursor.
I created cursor observable as follows, please check if this is the correct:
Observable<Cursor> cursorObservable = Observable.create(new ObservableOnSubscribe<Cursor>() {
#Override
public void subscribe(ObservableEmitter<Cursor> e) throws Exception {
SQLDbHelper dbHelper = SQLDbHelper.getInstance(ctx);
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from " + MoviesContract.MovieEntry.TABLE_NAME, null);
if (cursor != null) {
try {
while (cursor.moveToNext() && !e.isDisposed()) {
e.onNext(cursor);
}
} catch (Exception exception) {
e.onError(exception);
} finally {
cursor.close();
}
}
if (!e.isDisposed()) {
e.onComplete();
}
}
});

I thank that you will have better results wrapping the rows into a Map and passing it through the stream than passing cursor itself.
class SimpleTest {
#Test
fun testCursorStream() {
val cursor = fakeCursor()
val stream = getCursorStream(cursor)
stream.subscribe {
Log.d("Test", it.entries.toString())
}
}
private fun fakeCursor() : Cursor {
val columns = arrayOf("id", "name", "age")
val cursor = MatrixCursor(columns)
val row1 = arrayOf(1, "Rodrigo", 26L)
val row2 = arrayOf(2, "Lucas", 23L)
val row3 = arrayOf(3, "Alan", 26L)
cursor.addRow(row1)
cursor.addRow(row2)
cursor.addRow(row3)
return cursor
}
private fun getCursorStream(cursor: Cursor) : Observable<Map<String, Any?>> {
return Observable.create<Map<String, Any?>> {
try {
if (!cursor.moveToFirst()) {
it.onCompleted()
return#create
}
val row = HashMap<String, Any?>()
do {
val lastColumnIndex = cursor.columnCount - 1
for (index in 0..lastColumnIndex) {
val name = cursor.getColumnName(index)
val type = cursor.getType(index)
when (type) {
Cursor.FIELD_TYPE_STRING -> row.put(name, cursor.getString(index))
Cursor.FIELD_TYPE_BLOB -> row.put(name, cursor.getBlob(index))
Cursor.FIELD_TYPE_FLOAT -> row.put(name, cursor.getFloat(index))
Cursor.FIELD_TYPE_INTEGER -> row.put(name, cursor.getInt(index))
Cursor.FIELD_TYPE_NULL -> row.put(name, null)
}
}
it.onNext(row)
} while (cursor.moveToNext())
it.onCompleted()
} catch (e: Exception) {
it.onError(e)
}
}
}
}
Hope that it helps.

Related

avoid fragment recreation BottomNavigationView

i want to avoid fragment recreation by using BottomNavigationView
i read about that FragmentTransaction.replace is the problem and changing to add would help, but that didn't work for me..
maybe you can see here where i'm wrong
this is my Host Activity which hosting 3 fragments with BottomNavigationView
'''
class Host : AppCompatActivity() {
var fragment: Fragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
//for hide the actionBar (contains Fragment name) on top of the screen
val actionBar = supportActionBar
actionBar?.hide()
val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
val navView = findViewById<BottomNavigationView>(R.id.nav_view)
navView?.setupWithNavController(navController)
NavigationUI.setupWithNavController(navView, navController)
mAdapter = NfcAdapter.getDefaultAdapter(this)
fragmentStayAlive()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
for (fragment in supportFragmentManager.fragments) {
fragment.onActivityResult(requestCode, resultCode, data)
}
}
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
private fun fragmentStayAlive(){
val fragment1: Fragment = LobbyFragment()
val fragment2: Fragment = GameSessionFragment()
val fragment3: Fragment = UserStatusFragment()
val fm: FragmentManager = supportFragmentManager
var active = fragment1
fm.beginTransaction().add(R.id.nav_host_fragment, fragment3, "UserStatusFragment").hide(
fragment3
).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment2, "GameSessionFragment").hide(
fragment2
).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment1, "LobbyFragment").commit();
val mOnNavigationItemSelectedListener: BottomNavigationView.OnNavigationItemSelectedListener =
object : BottomNavigationView.OnNavigationItemSelectedListener {
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.getItemId()) {
R.id.lobbyFragment -> {
fm.beginTransaction().hide(active).show(fragment1).commit()
active = fragment1
return true
}
R.id.gameSessionFragment -> {
fm.beginTransaction().hide(active).show(fragment2).commit()
active = fragment2
return true
}
R.id.userStatusFragment -> {
fm.beginTransaction().hide(active).show(fragment3).commit()
active = fragment3
return true
}
}
replaceFragment(active, null, true, true)
return false
}
}
}
fun replaceFragment(
fragment: Fragment,
#Nullable bundle: Bundle?,
popBackStack: Boolean,
findInStack: Boolean
) {
val fm = supportFragmentManager
val ft: FragmentTransaction = fm.beginTransaction()
val tag = fragment.javaClass.name
val parentFragment: Fragment?
parentFragment = if (findInStack && fm.findFragmentByTag(tag) != null) {
fm.findFragmentByTag(tag)
} else {
fragment
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null) {
parentFragment!!.arguments = bundle
} else {
parentFragment!!.arguments = null
}
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
ft.addToBackStack(parentFragment.javaClass.name + "")
}
ft.add(R.id.nav_view, parentFragment, tag)
ft.commit()
fm.executePendingTransactions()
}
'''
thanks for all the helpers!

How to paginate live data that i am taking from cloud firestore?

I am building a chat app and displaying the text messages with the code below.I wanna know how i can paginate this live data that i am getting from cloud firestore so that a user can only look at the past say 30 messages using the paging 3.0 library.
ChatLogViewmodel
fun listenForMessages(user: User): LiveData<List<String>> {
val toId = user.uid
val betweenList = mutableListOf(Id(fromId), Id(toId))
val sortedList = betweenList.sortedBy { it.Id }
repository.getChatReference()?.whereEqualTo("between", sortedList)
?.addSnapshotListener { documents, e ->
if (e != null) {
return#addSnapshotListener
}
if (documents != null) {
for (document in documents) {
docId = document.id
docids.clear()
docids.add(docId)
}
}
docList.value = docids
}
return docList
}
fun listener(docId: String): LiveData<List<ChatMessage>> {
repository.getChatReference()?.document(docId)?.collection("Messages")?.orderBy(
"timestamp",
Query.Direction.ASCENDING
)?.addSnapshotListener { snap, e ->
if (e != null) {
return#addSnapshotListener
}
if (snap != null) {
messages.clear()
for (doc in snap.documents) {
val chatMessage = doc.toObject(ChatMessage::class.java)
if (chatMessage != null) {
messages.add(chatMessage)
isScrollable.value = true
}
}
}
messageList.value = messages
}
return messageList
}
ChatLogActivity
viewModel.listenForMessages(user).observe(this, {
it.forEach {doc->
viewModel.listener(doc).observe(this, { chatMessages->
binding.chatLogRecycler.adapter = ChatAdapter(chatMessages)
if (binding.chatLogRecycler.adapter!==null)
binding.chatLogRecycler.scrollToPosition(((binding.chatLogRecycler.adapter)?.itemCount!!) -1)
})
}
})
Repository
class ChatRepository #Inject constructor() {
fun getUserReference(uid:String): DocumentReference? {
return Firebase.firestore.collection("Users").document(uid)
}
fun getChatReference(): CollectionReference? {
return Firebase.firestore.collection("ChatChannels")
}
}

Kotlin searchView does not reload Firebase array

I have one activity with 2 recyclerViews, both recyclers are used with Firebase. One of the recyclers displays the results of a query, the other recycler has a listener that updates every time there is an update in Firebase. I added a searchView to filter the results from Firebase. The issue I'm having is when I'm trying to search the results from the Firebase query, when I start typing I see results but when I click the X to stop searching, the adapter does not reload the array and i don't see the list of items unless I reload the activity. I'm not sure what I'm missing here. Any help/suggestion is greatly appreciated. Here is my code:
Adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.eduardoz.ezmdapp.Model.Charges
import com.eduardoz.ezmdapp.R
class ChargesAdapter (private var charges: ArrayList<Charges>
, private var chargesAll: ArrayList<Charges>
, private val itemClick: (Charges) -> Unit)
: RecyclerView.Adapter<ChargesAdapter.ViewHolder>()
, Filterable {
inner class ViewHolder(itemView: View, val itemClick: (Charges) -> Unit) :
RecyclerView.ViewHolder(itemView) {
private val chargeCode = itemView.findViewById<TextView>(R.id.chargeCodeTxt)
private val chargeDescription = itemView.findViewById<TextView>(R.id.chargeDescriptionTxt)
fun bindCharges(charges: Charges) {
chargeCode?.text = charges.chargeCode
chargeDescription?.text = charges.chargeDescription
itemView.setOnClickListener { itemClick(charges) }
}
}
init {
this.charges = charges
chargesAll = java.util.ArrayList(charges)
}
override fun getItemCount(): Int {
return charges.count()
}
override fun onBindViewHolder(holder: ChargesAdapter.ViewHolder, position: Int) {
holder.bindCharges(charges[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChargesAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.charges_list, parent, false)
return ViewHolder(view, itemClick)
}
override fun getFilter(): Filter {
return searchFilter
}
private val searchFilter: Filter = object: Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList: ArrayList<Charges> = ArrayList()
if (constraint!!.isEmpty()) {
filteredList.addAll(chargesAll)
} else {
for(item in chargesAll) {
if
(item.chargeDescription.toLowerCase().contains(constraint.toString().toLowerCase())) {
filteredList.add(item)
}
}
}
val searchResults = FilterResults()
searchResults.values = filteredList
return searchResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
charges.clear()
charges.addAll(results!!.values as Collection<Charges>)
notifyDataSetChanged()
}
}
}
Activity
descriptionSearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText.isNotEmpty()) {
searchViewBar(newText)
} else {
if (newText.isEmpty()) { //I ADDED THIS TO RELOAD THE ADAPTER
charges.clear()
chargeList()
}
}
return false
}
})
private fun searchViewBar(newText: String) {
chargesListener = chargesCollectionRef
.whereGreaterThanOrEqualTo(CHARGE_DESCRIPTION, newText)
.whereLessThanOrEqualTo(CHARGE_DESCRIPTION, newText+"z")
.addSnapshotListener(this) { snapshot, exception ->
if (exception != null) {
println("error")
}
if (snapshot != null) {
charges.clear()
parseData(snapshot)
}
}
}
fun parseData(snapshot: QuerySnapshot) {
for (document in snapshot.documents) {
val data = document.data
val chargeCode = data!![CHARGE_CODE] as String
val chargeDescription = data[CHARGE_DESCRIPTION] as String
val chargeSpecialty = data[CHARGE_SPECIALTY] as String
val newChargeList = Charges(chargeCode, chargeDescription, chargeSpecialty)
charges.add(newChargeList)
}
chargesFromAdapter.notifyDataSetChanged()
}

How to save LocalData in Firebase Realtime database?

I am very new using Kotlin and programming and I am currently making a calendar with events. My problem comes when I want to connect these events to firebase.
I am using an example that I found in git (https://github.com/kizitonwose/CalendarView) that uses the ThreeTen library for dates. This is the Event object:
class Event (val id: String, val text: String, val date: LocalDate) : Serializable
The data variable is of the LocalData type and this is what is causing me problems since it seems that Firebase only accepts variables of type String, Int, etc ...
I tried to pass the variable to String with toString and with Gson (), without success.
Here is the code if it helps
private val inputDialog by lazy {
val editText = AppCompatEditText(requireContext())
val layout = FrameLayout(requireContext()).apply {
// Setting the padding on the EditText only pads the input area
// not the entire EditText so we wrap it in a FrameLayout.
setPadding(20, 20, 20, 20)
addView(editText, FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
}
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.example_3_input_dialog_title))
.setView(layout)
.setPositiveButton(R.string.save) { _, _ ->
saveEvent(editText.text.toString())
// Prepare EditText for reuse.
editText.setText("")
}
.setNegativeButton(R.string.close, null)
.create()
.apply {
setOnShowListener {
// Show the keyboard
editText.requestFocus()
context.inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
}
setOnDismissListener {
// Hide the keyboard
context.inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
}
}
}
private var selectedDate: LocalDate? = null
private val today = LocalDate.now()
private val titleSameYearFormatter = DateTimeFormatter.ofPattern("MMMM")
private val titleFormatter = DateTimeFormatter.ofPattern("MMM yyyy")
private val selectionFormatter = DateTimeFormatter.ofPattern("yyyy MM dd")
private val events = mutableMapOf<LocalDate, List<Event>>()
private var prueba = Gson().toJson(events)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_calendar, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mDatabaseReference = mDatabase!!.reference.child("events")
exThreeRv.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
exThreeRv.adapter = eventsAdapter
exThreeRv.addItemDecoration(DividerItemDecoration(requireContext(), RecyclerView.VERTICAL))
val daysOfWeek = daysOfWeekFromLocale()
val currentMonth = YearMonth.now()
exThreeCalendar.setup(currentMonth.minusMonths(10), currentMonth.plusMonths(10), daysOfWeek.first())
exThreeCalendar.scrollToMonth(currentMonth)
if (savedInstanceState == null) {
exThreeCalendar.post {
// Show today's events initially.
selectDate(today)
}
}
class DayViewContainer(view: View) : ViewContainer(view) {
lateinit var day: CalendarDay // Will be set when this container is bound.
val textView = view.exThreeDayText
val dotView = view.exThreeDotView
init {
view.setOnClickListener {
if (day.owner == DayOwner.THIS_MONTH) {
selectDate(day.date)
}
}
}
}
exThreeCalendar.dayBinder = object : DayBinder<DayViewContainer> {
override fun create(view: View) = DayViewContainer(view)
override fun bind(container: DayViewContainer, day: CalendarDay) {
container.day = day
val textView = container.textView
val dotView = container.dotView
textView.text = day.date.dayOfMonth.toString()
if (day.owner == DayOwner.THIS_MONTH) {
textView.makeVisible()
when (day.date) {
today -> {
textView.setTextColorRes(R.color.white)
textView.setBackgroundResource(R.drawable.today_bg)
dotView.makeInVisible()
}
selectedDate -> {
textView.setTextColorRes(R.color.white)
textView.setBackgroundResource(R.drawable.selected_bg)
dotView.makeInVisible()
}
else -> {
textView.setTextColorRes(R.color.black)
textView.background = null
dotView.isVisible = events[day.date].orEmpty().isNotEmpty()
}
}
} else {
textView.makeInVisible()
dotView.makeInVisible()
}
}
}
exThreeCalendar.monthScrollListener = {
requireActivity().home.text = if (it.year == today.year) {
titleSameYearFormatter.format(it.yearMonth)
} else {
titleFormatter.format(it.yearMonth)
}
// Select the first day of the month when
// we scroll to a new month.
selectDate(it.yearMonth.atDay(1))
}
class MonthViewContainer(view: View) : ViewContainer(view) {
val legendLayout = view.legendLayout
}
exThreeCalendar.monthHeaderBinder = object : MonthHeaderFooterBinder<MonthViewContainer> {
override fun create(view: View) = MonthViewContainer(view)
override fun bind(container: MonthViewContainer, month: CalendarMonth) {
// Setup each header day text if we have not done that already.
if (container.legendLayout.tag == null) {
container.legendLayout.tag = month.yearMonth
container.legendLayout.children.map { it as TextView }.forEachIndexed { index, tv ->
tv.text = daysOfWeek[index].name.first().toString()
tv.setTextColorRes(R.color.black)
}
}
}
}
exThreeAddButton.setOnClickListener {
inputDialog.show()
}
}
private fun selectDate(date: LocalDate) {
if (selectedDate != date) {
val oldDate = selectedDate
selectedDate = date
oldDate?.let { exThreeCalendar.notifyDateChanged(it) }
exThreeCalendar.notifyDateChanged(date)
updateAdapterForDate(date)
}
}
private fun saveEvent(text: String) {
if (text.isBlank()) {
Toast.makeText(requireContext(),
R.string.example_3_empty_input_text, Toast.LENGTH_LONG).show()
} else {
selectedDate?.let {
events[it] = events[it].orEmpty().plus(
Event(
UUID.randomUUID().toString(),
text,
it
)
)
uploadFirebase()
updateAdapterForDate(it)
}
}
}
private fun deleteEvent(event: Event) {
val date = event.date
events[date] = events[date].orEmpty().minus(event)
updateAdapterForDate(date)
}
private fun updateAdapterForDate(date: LocalDate) {
eventsAdapter.events.clear()
eventsAdapter.events.addAll(events[date].orEmpty())
eventsAdapter.notifyDataSetChanged()
exThreeSelectedDateText.text = selectionFormatter.format(date)
}
fun uploadFirebase(){
val newEvent = mDatabaseReference.push()
newEvent.setValue(events)
}
override fun onStart() {
super.onStart()
}
override fun onStop() {
super.onStop()
}
}
There is no way you can add a property of type LocalDate in a Firebase Realtime database because it is not a supported data-type. However, there are two ways in which you can solve this:
You save the date as a ServerValue.TIMESTAMP, which basically means that you save the number of seconds that have elapsed since the Unix epoch. In this case, the server writes the current date in the database. To achieve this, please see my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
You specify a custom long value for your date field. In this case, it's up to you to determine what date is written.
Unfortunately, there is no way you can combine these two options, you can use one or the other.
When talking about a LocalDate, we usually talk about an offset, in which case, this what I'll do. I'll store a Timestamp property, as explained at point one, that will let the server populate with the server Timestamp, as well as an offset property, that should be populated with the offset in days/hours.

Create Multimedia component with Metadata fields.using core service

I am creating Multimedia components using core service and everything is working fine. But when Metadata schema fields are defined on the Multimedia schema using which I am creating my Multimedia components then I am getting following error:-
Unable to find http://www.tridion.com/ContentManager/5.0/DefaultMultimediaSchema:Metadata.
This message is displayed when I have given Default Multimedia schema's TCM ID for Multimedia component. As metadata fields are saved in Tridion Database so I first have to retrieve these fields from broker or what is the best solution for this, please suggest. Below is the sample code. Please modify it if someone have any idea for providing default value for metadatafields and how to retrieve them (with/without querying broker DB):-
public static string UploadMultiMediaComponent(string folderUri, string title, string schemaID)
{
core_service.ServiceReference1.SessionAwareCoreService2010Client client = new SessionAwareCoreService2010Client();
client.ClientCredentials.Windows.ClientCredential.UserName = "myUserName";
client.ClientCredentials.Windows.ClientCredential.Password = "myPassword"; client.Open();
ComponentData multimediaComponent = (ComponentData)client.GetDefaultData(
ItemType.Component, folderUri);
multimediaComponent.Title = title;
multimediaComponent.ComponentType = ComponentType.Multimedia;
multimediaComponent.Schema.IdRef =schemaID;
//multimediaComponent.Metadata = "";
StreamUpload2010Client streamClient = new StreamUpload2010Client();
FileStream objfilestream = new FileStream(#"\My Documents\images.jpg",
FileMode.Open, FileAccess.Read);
string tempLocation = streamClient.UploadBinaryContent("images.jpg",
objfilestream);
BinaryContentData binaryContent = new BinaryContentData();
binaryContent.UploadFromFile = tempLocation;
binaryContent.Filename = "images.jpg";
binaryContent.MultimediaType = new LinkToMultimediaTypeData()
{
// for jpg file
IdRef = "tcm:0-2-65544"
};
multimediaComponent.BinaryContent = binaryContent;
IdentifiableObjectData savedComponent = client.Save(multimediaComponent,
new ReadOptions());
client.CheckIn(savedComponent.Id, null);
streamClient.Close();
client.Close();
Console.WriteLine(savedComponent.Id);
//}
}
I don't know why your code not working but following code is working for me
public static ComponentData GenerateMultiMediaComponent(TridionGeneration tridionGeneration, XmlData newsArticle, string componentName)
{
try
{
Dictionary<string, object> dicTridion = Common.GetTridionObject(tridionGeneration.client, ItemType.Component, tridionGeneration.Settings.ComponentFolderUri, componentName);
int objectCount = (int)dicTridion["count"];
SchemaFieldsData schemaFields = tridionGeneration.client.ReadSchemaFields(tridionGeneration.Settings.SchemaUri, true, new ReadOptions());
ComponentData componentData = (ComponentData)tridionGeneration.client.GetDefaultData(ItemType.Component, tridionGeneration.Settings.ComponentFolderUri);
if (schemaFields.Fields != null)
{
var fields = Fields.ForContentOf(schemaFields);
Helper.FillSchemaFields(tridionGeneration, fields);
componentData.Content = fields.ToString();
}
if (schemaFields.MetadataFields != null)
{
var metafields = Fields.ForMetadataOf(schemaFields, componentData);
Helper.FillSchemaFields(tridionGeneration, metafields);
componentData.Metadata = metafields.ToString();
}
componentData.Title = (objectCount == 0) ? componentName : componentName + " " + (objectCount + 1).ToString();
componentData.ComponentType = ComponentType.Multimedia;
StreamUpload2010Client streamClient = new StreamUpload2010Client();
FileStream objfilestream = new FileStream(#"[IMAGE_PATH]", FileMode.Open, FileAccess.Read);
string tempLocation = streamClient.UploadBinaryContent("images.jpg", objfilestream);
BinaryContentData binaryContent = new BinaryContentData();
binaryContent.UploadFromFile = tempLocation;
binaryContent.Filename = "[IMAGE_NAME]";
componentData.BinaryContent = binaryContent;
binaryContent.MultimediaType = new LinkToMultimediaTypeData()
{
IdRef = "tcm:0-2-65544"
};
componentData = (ComponentData)tridionGeneration.client.Create(componentData, new ReadOptions());
return componentData;
}
catch (Exception ex)
{
return null;
}
}
Here is the Helper class:
public static class Helper
{
public static void FillSchemaFields(TridionGeneration tridionGeneration, Fields fields)
{
List<XmlData> data = XmlHelper.xmlData;
var ofield = fields.GetEnumerator();
while (ofield.MoveNext())
{
Field f = ofield.Current;
FillFieldValue(tridionGeneration, fields, f, data[0]);
}
}
private static void FillFieldValue(TridionGeneration tridionGeneration, Fields fields, Field f, XmlData data)
{
if (f.Type == typeof(MultimediaLinkFieldDefinitionData))
{
fields[f.Name].Value = tridionGeneration.Settings.DefaultImageUri;
}
else if (f.Type != typeof(EmbeddedSchemaFieldDefinitionData))
{
foreach (XmlData fieldvalue in data.Attributes)
{
if (f.Type == typeof(DateFieldDefinitionData))
{
if (fieldvalue.text.ToLower() == f.Name.ToLower())
{
fields[f.Name].Value = Convert.ToDateTime(fieldvalue.value).ToString("yyyy-MM-ddTHH:mm:ss");
}
else
{
string val = FindSchemaValue(tridionGeneration, fieldvalue.Attributes, f.Name);
if (!string.IsNullOrEmpty(val))
{
fields[f.Name].Value = Convert.ToDateTime(val).ToString("yyyy-MM-ddTHH:mm:ss");
}
}
}
else
{
if (fieldvalue.text.ToLower() == f.Name.ToLower())
{
fields[f.Name].Value = System.Net.WebUtility.HtmlEncode(fieldvalue.value);
}
else
{
string val = FindSchemaValue(tridionGeneration, fieldvalue.Attributes, f.Name);
if (!string.IsNullOrEmpty(val))
{
fields[f.Name].Value = System.Net.WebUtility.HtmlEncode(val);
}
}
}
}
}
else
{
Fields fs = f.GetSubFields();
var ofield = fs.GetEnumerator();
while (ofield.MoveNext())
{
Field ff = ofield.Current;
FillFieldValue(tridionGeneration, fs, ff, data);
}
}
}
private static string FindSchemaValue(TridionGeneration tridionGeneration, List<XmlData> data, string fieldname)
{
foreach (XmlData fieldvalue in data)
{
if (fieldvalue.text.ToLower() == fieldname.ToLower())
{
return fieldvalue.value;
}
else
{
FindSchemaValue(tridionGeneration, fieldvalue.Attributes, fieldname);
}
}
return "";
}
}
and the Fields class:
public class Fields
{
private ItemFieldDefinitionData[] definitions;
private XmlNamespaceManager namespaceManager;
private XmlElement root; // the root element under which these fields live
// at any point EITHER data OR parent has a value
private SchemaFieldsData data; // the schema fields data as retrieved from the core service
private Fields parent; // the parent fields (so we're an embedded schema), where we can find the data
public Fields(SchemaFieldsData _data, ItemFieldDefinitionData[] _definitions, string _content = null, string _rootElementName = null)
{
data = _data;
definitions = _definitions;
var content = new XmlDocument();
if (!string.IsNullOrEmpty(_content))
{
content.LoadXml(_content);
}
else
{
content.AppendChild(content.CreateElement(string.IsNullOrEmpty(_rootElementName) ? _data.RootElementName : _rootElementName, _data.NamespaceUri));
}
root = content.DocumentElement;
namespaceManager = new XmlNamespaceManager(content.NameTable);
namespaceManager.AddNamespace("custom", _data.NamespaceUri);
}
public Fields(Fields _parent, ItemFieldDefinitionData[] _definitions, XmlElement _root)
{
definitions = _definitions;
parent = _parent;
root = _root;
}
public static Fields ForContentOf(SchemaFieldsData _data)
{
return new Fields(_data, _data.Fields);
}
public static Fields ForContentOf(SchemaFieldsData _data, ComponentData _component)
{
return new Fields(_data, _data.Fields, _component.Content);
}
public static Fields ForMetadataOf(SchemaFieldsData _data, RepositoryLocalObjectData _item)
{
return new Fields(_data, _data.MetadataFields, _item.Metadata, "Metadata");
}
public string NamespaceUri
{
get { return data != null ? data.NamespaceUri : parent.NamespaceUri; }
}
public XmlNamespaceManager NamespaceManager
{
get { return parent != null ? parent.namespaceManager : namespaceManager; }
}
internal IEnumerable<XmlElement> GetFieldElements(ItemFieldDefinitionData definition)
{
return root.SelectNodes("custom:" + definition.Name, NamespaceManager).OfType<XmlElement>();
}
internal XmlElement AddFieldElement(ItemFieldDefinitionData definition)
{
var newElement = root.OwnerDocument.CreateElement(definition.Name, NamespaceUri);
XmlNodeList nodes = root.SelectNodes("custom:" + definition.Name, NamespaceManager);
XmlElement referenceElement = null;
if (nodes.Count > 0)
{
referenceElement = (XmlElement)nodes[nodes.Count - 1];
}
else
{
// this is the first value for this field, find its position in the XML based on the field order in the schema
bool foundUs = false;
for (int i = definitions.Length - 1; i >= 0; i--)
{
if (!foundUs)
{
if (definitions[i].Name == definition.Name)
{
foundUs = true;
}
}
else
{
var values = GetFieldElements(definitions[i]);
if (values.Count() > 0)
{
referenceElement = values.Last();
break; // from for loop
}
}
} // for every definition in reverse order
} // no existing values found
root.InsertAfter(newElement, referenceElement); // if referenceElement is null, will insert as first child
return newElement;
}
public IEnumerator<Field> GetEnumerator()
{
return (IEnumerator<Field>)new FieldEnumerator(this, definitions);
}
public Field this[string _name]
{
get
{
var definition = definitions.First<ItemFieldDefinitionData>(ifdd => ifdd.Name == _name);
if (definition == null) throw new ArgumentOutOfRangeException("Unknown field '" + _name + "'");
return new Field(this, definition);
}
}
public override string ToString()
{
return root.OuterXml;
}
}
public class FieldEnumerator : IEnumerator<Field>
{
private Fields fields;
private ItemFieldDefinitionData[] definitions;
// Enumerators are positioned before the first element until the first MoveNext() call
int position = -1;
public FieldEnumerator(Fields _fields, ItemFieldDefinitionData[] _definitions)
{
fields = _fields;
definitions = _definitions;
}
public bool MoveNext()
{
position++;
return (position < definitions.Length);
}
public void Reset()
{
position = -1;
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public Field Current
{
get
{
try
{
return new Field(fields, definitions[position]);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Dispose()
{
}
}
public class Field
{
private Fields fields;
private ItemFieldDefinitionData definition;
public Field(Fields _fields, ItemFieldDefinitionData _definition)
{
fields = _fields;
definition = _definition;
}
public string Name
{
get { return definition.Name; }
}
public Type Type
{
get { return definition.GetType(); }
}
public string Value
{
get
{
return Values.Count > 0 ? Values[0] : null;
}
set
{
if (Values.Count == 0) fields.AddFieldElement(definition);
Values[0] = value;
}
}
public ValueCollection Values
{
get
{
return new ValueCollection(fields, definition);
}
}
public void AddValue(string value = null)
{
XmlElement newElement = fields.AddFieldElement(definition);
if (value != null) newElement.InnerText = value;
}
public void RemoveValue(string value)
{
var elements = fields.GetFieldElements(definition);
foreach (var element in elements)
{
if (element.InnerText == value)
{
element.ParentNode.RemoveChild(element);
}
}
}
public void RemoveValue(int i)
{
var elements = fields.GetFieldElements(definition).ToArray();
elements[i].ParentNode.RemoveChild(elements[i]);
}
public IEnumerable<Fields> SubFields
{
get
{
var embeddedFieldDefinition = definition as EmbeddedSchemaFieldDefinitionData;
if (embeddedFieldDefinition != null)
{
var elements = fields.GetFieldElements(definition);
foreach (var element in elements)
{
yield return new Fields(fields, embeddedFieldDefinition.EmbeddedFields, (XmlElement)element);
}
}
}
}
public Fields GetSubFields(int i = 0)
{
var embeddedFieldDefinition = definition as EmbeddedSchemaFieldDefinitionData;
if (embeddedFieldDefinition != null)
{
var elements = fields.GetFieldElements(definition);
if (i == 0 && !elements.Any())
{
// you can always set the first value of any field without calling AddValue, so same applies to embedded fields
AddValue();
elements = fields.GetFieldElements(definition);
}
return new Fields(fields, embeddedFieldDefinition.EmbeddedFields, elements.ToArray()[i]);
}
else
{
throw new InvalidOperationException("You can only GetSubField on an EmbeddedSchemaField");
}
}
// The subfield with the given name of this field
public Field this[string name]
{
get { return GetSubFields()[name]; }
}
// The subfields of the given value of this field
public Fields this[int i]
{
get { return GetSubFields(i); }
}
}
Can you try this?
multimediaComponent.Metadata = "<Metadata/>";

Resources