MediabrolForm.cs
Description du code
MediabrolForm.cs est un fichier du projet BiblioBrol.Ce fichier est situé dans /var/www/bin/sniplets/bibliobrol/src/.
Projet BiblioBrol :
Gestion de media en CSharp.
Pour plus d'infos, vous pouvez consulter la brève analyse.
Code source ou contenu du fichier
Code c# (MediabrolForm.cs) (841 lignes)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using be.gaudry.bibliobrol.model.dao; using be.gaudry.bibliobrol.model; using be.gaudry.bibliobrol.view.utils; using be.gaudry.bibliobrol.view.dialogs; using be.gaudry.observer; using be.gaudry.view; using be.gaudry.view.utils; namespace be.gaudry.bibliobrol.view { public partial class MediabrolForm : MDIChildForm, IObserver, IMDIChild { #region constructors and declarations /// <summary> /// used to get the "mouse over cell" for the context menu /// </summary> private DataGridViewCellEventArgs mouseLocation; private BrolType brolType; private MediaBrol mediabrol; private const String BORROW_START = "Emprunter"; private const String BORROW_STOP = "Rentrer"; private List<DAOUtils.BORROW_FIELD> borrowFields; private BrolPropertiesForm brolPropertiesForm; private MediabrolPropertiesForm mediabrolPropertiesForm; public MediabrolForm() { ModelAdapter.Instance.addObserver(this); Splasher.Status = "Initialisation des composants"; InitializeComponent(); this.SuspendLayout(); searchControl.setDataGridView(brolsDGV); Splasher.Status = "Initialisation des menus"; addToolStrip(mediabrolTS); addToolStrip(borrowsTS); //BORROW_START = "Emprunter"; //BORROW_STOP = "Rentrer"; Splasher.Status = "Initialisation des emprunts"; initBorrowFields(); mediaCB.DisplayMember = "Name"; mediaCB.ValueMember = "Id"; Splasher.Status = "Chargement du module d'exportation"; importExportCtl.setBIblioItemForm(this); Splasher.Status ="Affichage des composants"; this.ResumeLayout(false); } #endregion #region initializations private void initBorrowFields() { borrowFields.Add(DAOUtils.BORROW_FIELD.borrower); borrowFields.Add(DAOUtils.BORROW_FIELD.comment); borrowFields.Add(DAOUtils.BORROW_FIELD.endDate); borrowFields.Add(DAOUtils.BORROW_FIELD.planDate); borrowFields.Add(DAOUtils.BORROW_FIELD.startDate); } #endregion #region getters /// <summary> /// Ids of mediabrols displayed into left datagridview /// </summary> public List<int> MediabrolIds { get { foreach (DataGridViewRow row in brolsDGV.Rows) { ids.Add((int)row.Cells["mbId"].Value); } return ids; } } /// <summary> /// Ids of brols displayed into left datagridview /// </summary> public List<int> BrolIds { get { foreach (DataGridViewRow row in brolsDGV.Rows) { ids.Add((int)row.Cells["brolId"].Value); } return ids; } } #endregion #region datagridview private void selectItem() { /*try { int id = (int)brolsDGV.Rows[mouseLocation.RowIndex].Cells["mbId"].Value; mediabrol = ModelAdapter.loadMediaBrol(id, false); fillMediaBrolInfos(); if (mainTC.SelectedTab.Name.Equals("borrowTP")) fillBorrowsDGV(); } catch (Exception ex) { StaticObservable.notify(new Notification(Notification.VERBOSE.lowError, "Aucun media sélectionné", ex, this)); }*/ selectItem((int)brolsDGV.Rows[mouseLocation.RowIndex].Cells["mbId"].Value); } private void selectItem(int id) { try { //int id = (int)brolsDGV.Rows[mouseLocation.RowIndex].Cells["mbId"].Value; mediabrol = ModelAdapter.loadMediaBrol(id, false); fillMediaBrolInfos(); if (mainTC.SelectedTab.Name.Equals("borrowTP")) fillBorrowsDGV(); startBorrowCMSTSMenuItem.Enabled = !mediabrol.Borrowed; startBorrowTSB.Enabled = !mediabrol.Borrowed; startBorrowTSMenuItem.Enabled = !mediabrol.Borrowed; stopBorrowCMSTSMenuItem.Enabled = mediabrol.Borrowed; stopBorrowTSB.Enabled = mediabrol.Borrowed; stopBorrowTSMenuItem.Enabled = mediabrol.Borrowed; } catch (Exception ex) { StaticObservable.notify(new Notification(Notification.VERBOSE.lowError, "Aucun media sélectionné", ex, this)); } } private void mediabrolsDGV_SelectionChanged(object sender, EventArgs e) { try { selectItem((int)brolsDGV.CurrentRow.Cells["mbId"].Value); } catch (NullReferenceException) { } } private void mediabrolsDGV_CellClick(object sender, DataGridViewCellEventArgs e) { selectItem((int)brolsDGV.CurrentRow.Cells["mbId"].Value); } private void mediabrolsDGV_CellMouseEnter(object sender, DataGridViewCellEventArgs e) { mouseLocation = e; } private void mediabrolsDGVCMS_Opening(object sender, CancelEventArgs e) { if (mouseLocation.RowIndex < 0) { e.Cancel = true; } else { int id = (int)brolsDGV.Rows[mouseLocation.RowIndex].Cells["mbId"].Value; infoTSMenuItem.Text = String.Format( "Exemplaire {0}", id ); if (mouseLocation.RowIndex != brolsDGV.CurrentRow.Index) { brolsDGV.CurrentRow.Selected = false; brolsDGV.Rows[mouseLocation.RowIndex].Selected = true; selectItem(id); } } } private void openWithTSMenuItem_Click(object sender, EventArgs e) { selectItem(); mainTC.SelectedTab = mediabrolTP; } private void onBrolInfo_Click(object sender, EventArgs e) { selectItem(); if (mediabrol.Brol != null) { brolPropertiesForm.Brol = mediabrol.Brol; brolPropertiesForm.ShowDialog(this); } } private void onMediabrolInfo_Click(object sender, EventArgs e) { selectItem(); if (mediabrol != null) { mediabrolPropertiesForm.MediaBrol = mediabrol; mediabrolPropertiesForm.ShowDialog(this); } } private void fillMediaBrolInfos() { bNameTB.Text = mediabrol.Name; bLocTB.Text = mediabrol.Localisation; bComTB.Text = mediabrol.Comment; mediaCB.SelectedIndex = -1; mediaCB.Items.Clear(); mediaCB.Items.AddRange(ModelAdapter.loadMedias(mediabrol.Brol.BrolType.Id).ToArray()); if (mediabrol.MediaType == null || mediabrol.MediaType.Id < 1) { mediaCB.SelectedIndex = -1; //editSpMediaCB.ResetText(); mediaCB.Refresh(); } else { mediaCB.SelectedItem = mediabrol.MediaType; } qualitiesAddCB.Items.Clear(); qualitiesAddCB.Items.AddRange(ModelAdapter.loadQualities(mediabrol.Brol.BrolType).ToArray()); qualitiesLB.Items.Clear(); qualitiesLB.Items.AddRange(mediabrol.Qualities.ToArray()); //qualitiesLB.DataSource = mediabrol.Qualities; brolInfoCtl.setBrol(mediabrol.Brol); fillOwnerFields(); { insertionDateDTP.Value = DateTime.Now; insertionDateDTP.Visible = false; insertionDateBtn.Visible = true; } else { insertionDateDTP.Value = mediabrol.InsertionDate; insertionDateDTP.Visible = true; insertionDateBtn.Visible = false; } } private void fillOwnerFields() { ownerLastNameTB.Text = mediabrol.Owner.LastName; ownerFirstNameTB.Text = mediabrol.Owner.FirstName; ownerImg.ImageLocation = Img.getPersImgPath(mediabrol.Owner); } #endregion #region borrows /// <summary> /// Start borrow for only one mediabrol /// </summary> /// <param name="borrower"></param> private void defineBorrower(Person borrower) { ModelAdapter.startBorrow(mediabrol.Id, borrower.Id); } /// <summary> /// Start borrow for selected mediabrols /// </summary> /// <param name="borrower"></param> private void defineBorrowerMulti(Person borrower) { foreach(DataGridViewRow row in brolsDGV.Rows) { if(!ModelAdapter.startBorrow((int)row.Cells["mbId"].Value, borrower.Id)) { errors.Add((String)row.Cells[searchControl.TitleColumnId].Value); } } if(errors.Count>0) { foreach (String s in errors) { str.AppendFormat("\n{0}", s); } DialogResult r = MessageBox.Show( this, str.ToString(), "Emprunts non réalisés", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1); } } /// <summary> /// If this item is borrowed, set end date to now, /// else insert new row (set start date to now) /// and save modifications to the persistant layer. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void borrowManageBtn_Click(object sender, EventArgs e) { if (mediabrol != null) { if (!mediabrol.Borrowed) { diaSelectPerson.SelectedRole = ModelAdapter.getUserRole(); if (brolsDGV.SelectedRows.Count > 1) { DialogResult q = MessageBox.Show( this, "Emprunter TOUS les exemplaires affichés ?\nNON pour emprunter seulement l'exemplaire sélectionné.", "Emprunts", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2 ); if (q == DialogResult.Yes) { diaSelectPerson.persSelected += new DiaSelectActor.SelectPersEventHandler(this.defineBorrowerMulti); } else { } } else { } diaSelectPerson.ShowDialog(); //todo : prompt for plandate? and comment ? //todo : if borrower has yet other not closed borrows, send a warning } else { if (brolsDGV.SelectedRows.Count > 1) { DialogResult q = MessageBox.Show( this, "Retour d'emprunt pour TOUS les exemplaires affichés ?\nNON pour rentrer seulement l'exemplaire sélectionné.", "Retours d'emprunts", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2 ); if (q == DialogResult.Yes) { MessageBox.Show( this, "La méthode n'est pas encore implémentée pour les sélections multiples.", "Retours d'emprunts", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1 ); } else { stopBorrow(); } } else { stopBorrow(); } } fillBorrowsDGV(); } else StaticObservable.notify(new Notification(Notification.VERBOSE.lowError, "Emprunts", "Aucun exemplaire sélectionné", this)); } private void stopBorrow() { try { //ModelAdapter.stopBorrow((int)borrowsDGV.Rows[0].Cells["id"].Value); ModelAdapter.stopBorrow(mediabrol); //todo : prompt to enter comment... } catch (Exception ex) { StaticObservable.notify(new Notification(Notification.VERBOSE.lowError, "Sélection d'emprunt", "Aucun emprunt à rentrer sélectionné", ex, this)); } } private void fillBorrowsDGV() { if (mediabrol != null && mediabrol.Id > 0) { borrowsDGV.DataSource = ModelAdapter.loadBorrows(mediabrol.Id, borrowFields); borrowsDGV.Visible = true; try { { borrowManageBtn.Image = global::be.gaudry.bibliobrol.Properties.Resources.brolBorrowClose; borrowManageBtn.Text = BORROW_STOP; } else { borrowManageBtn.Image = global::be.gaudry.bibliobrol.Properties.Resources.brolBorrowOpen; borrowManageBtn.Text = BORROW_START; } } catch (Exception ex2) { StaticObservable.notify(new Notification(Notification.VERBOSE.error, "Sélection d'emprunt", ex2, this)); } fillSelectedBorrowInfos(); } } /// <summary> /// Fill the form with infos from the selected borrow into borrowsDGV /// </summary> private void fillSelectedBorrowInfos() { String info = ""; String planDate = "Non spécifié"; if (borrowsDGV.Rows.Count > 0) { try { info = (String)borrowsDGV.SelectedRows[0].Cells["comment"].Value; planDate = ((DateTime)borrowsDGV.SelectedRows[0].Cells["planDate"].Value).ToShortDateString(); } catch (Exception ex) { StaticObservable.notify(new Notification(Notification.VERBOSE.error, "Sélection d'emprunt", ex, this)); } } borrowInfoRTB.Text = info; borrowPLanDateInfoLbl.Text = planDate; } private void borrowsDGV_CellClick(object sender, DataGridViewCellEventArgs e) { fillSelectedBorrowInfos(); } private void startBorrowTSMenuItem_Click(object sender, EventArgs e) { borrowManageBtn_Click(sender, e); } private void stopBorrowCMSTSMenuItem_Click(object sender, EventArgs e) { borrowManageBtn_Click(sender, e); } #endregion #region form private void BIblioItemForm_Enter(object sender, EventArgs e) { this.Icon = global::be.gaudry.bibliobrol.Properties.Resources.mediabrolIcon; //this.Icon = Icon.FromHandle(global::BiblioBrol.Properties.Resources.mediabrol_small.GetHicon()); } #endregion #region qualities private void qualityAddBtn_Click(object sender, EventArgs e) { qualitiesAddCB.Visible = true; } private void qualitiesAddCB_SelectedIndexChanged(object sender, EventArgs e) { if (qualitiesAddCB.SelectedItem != null) { Quality q = (Quality)qualitiesAddCB.SelectedItem; if (!qualitiesLB.Items.Contains(q)) { q.Status = STATUS.toAdd; qualitiesLB.Items.Add(q); } else { StaticObservable.notify(new Notification(Notification.VERBOSE.error, "La caractéristique " + q + " est déjà présente dans la liste", this)); } } qualitiesAddCB.Visible = false; } private void qualitiesLB_SelectedIndexChanged(object sender, EventArgs e) { if (qualitiesLB.SelectedIndex >= 0) qualityRemBtn.Enabled = true; } private void qualityRemBtn_Click(object sender, EventArgs e) { if (qualitiesLB.SelectedItem != null) { Quality q = (Quality)qualitiesLB.SelectedItem; q.Status = STATUS.toDelete; editCategoriesLB.Refresh(); qualityRemBtn.Enabled = false; } } private void qualitiesLB_DrawItem(object sender, DrawItemEventArgs e) { if (e.Index >= 0) { ListBox listBox = (ListBox)sender; Quality q = (Quality)listBox.Items[e.Index]; Brush brush;//forecolor Brush selBrushBkgnd;//background color switch (q.Status) { case STATUS.toAdd: brush = Brushes.ForestGreen; selBrushBkgnd = Brushes.LightGray; break; case STATUS.toDelete: brush = Brushes.Crimson; selBrushBkgnd = Brushes.Beige; break; default: brush = Brushes.Black; selBrushBkgnd = Brushes.White; break; } //selected item drawing : invert colors if (((e.State & DrawItemState.Selected) == DrawItemState.Selected)) { Brush temp = (brush.Equals(Brushes.Black)) ? Brushes.SteelBlue : brush; brush = selBrushBkgnd; selBrushBkgnd = temp; } else selBrushBkgnd = Brushes.White; e.Graphics.FillRectangle(selBrushBkgnd, e.Bounds); e.Graphics.DrawString(q.ToString(), listBox.Font, brush, e.Bounds.X, e.Bounds.Y); } } #endregion #region misceleanous mediabrol fct private void mainTC_SelectedIndexChanged(object sender, EventArgs e) { switch (mainTC.SelectedTab.Name) { case "borrowTP": //todo : fill only the first time fillBorrowsDGV(); enableBorrowsActions(true); enableMediabrolActions(false); enableSearchAction(false); break; case "searchTP": enableBorrowsActions(false); enableMediabrolActions(false); enableSearchAction(true); break; case "infoTP": enableBorrowsActions(false); enableMediabrolActions(false); enableSearchAction(false); break; case "mediabrolTP": enableBorrowsActions(false); enableMediabrolActions(true); enableSearchAction(false); break; case "exportTP": enableBorrowsActions(false); enableMediabrolActions(false); enableSearchAction(false); break; } } private void enableSearchAction(bool enabled) { showSearchResultTSB.Enabled = enabled; } private void enableMediabrolActions(bool enabled) { saveMediabrolTSB.Enabled = enabled; saveMediabrolTSMenuItem.Enabled = enabled; deleteMediabrolTSB.Enabled = enabled; deleteMediabrolTSMenuItem.Enabled = enabled; } private void enableBorrowsActions(bool enabled) { startBorrowTSB.Enabled = enabled; startBorrowTSMenuItem.Enabled = enabled; stopBorrowTSB.Enabled = enabled; stopBorrowTSMenuItem.Enabled = enabled; cleanBorrowTSB.Enabled = enabled; cleanBorrowTSMenuItem.Enabled = enabled; cleanAllBorrowsTSB.Enabled = enabled; cleanAllBorrowsTSMenuItem.Enabled = enabled; editBorrowTSB.Enabled = enabled; editBorrowTSMenuItem.Enabled = enabled; } private void setOwner(Person owner) { mediabrol.Owner = owner; fillOwnerFields(); } private void selectPersonBtn_Click(object sender, EventArgs e) { diaSelectPerson.ShowDialog(); } private void insertionDateBtn_Click(object sender, EventArgs e) { insertionDateBtn.Visible = false; insertionDateDTP.Visible = true; } private void remAllSelectedMediabrolsBtn_Click(object sender, EventArgs e) { DialogResult r = MessageBox.Show( this, "Supprimer l'ensemble des exemplaires affichés dans le panneau de gauche ?", "Suppression des exemplaires", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (r == DialogResult.Yes) { DialogResult r2 = MessageBox.Show( this, "Supprimer les ouvrages en même temps que les exemplaires ?", "Suppression des exemplaires", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); String title; foreach (DataGridViewRow row in brolsDGV.Rows) { try { title = (String)row.Cells[searchControl.TitleColumnId].Value; } catch (Exception) { title = ""; } ModelAdapter.deleteMediaBrol((int)row.Cells[0].Value, title, (r2 == DialogResult.Yes)); } } } #endregion #region manage mediabrol private void setMediabrolFromForm() { mediabrol.Name = bNameTB.Text; mediabrol.Localisation = bLocTB.Text; mediabrol.Comment = bComTB.Text; mediabrol.MediaType = (Media)mediaCB.SelectedItem; mediabrol.Qualities.Clear(); foreach (Object oQuality in qualitiesLB.Items) { try { mediabrol.Qualities.Add((Quality)oQuality); } catch (Exception cce) { StaticObservable.notify(new Notification(Notification.VERBOSE.error, "Impossible de charger les caractéristiques depuis le formulaire", cce, this)); } } mediabrol.InsertionDate = insertionDateDTP.Value; //mediabrol.Owner } #endregion #region search private void refreshResults_Click(object sender, EventArgs e) { if (this.Visible) searchControl.displaySearchResults(); } #endregion #region IObserver Membres public void update(Notification notification) { { searchControl.displaySearchResults(); } } #endregion #region actions private void searchMediabrolAction(object sender, EventArgs e) { mainTC.SelectedIndex = 0; this.Focus(); } private void deleteMediabrolAction(object sender, EventArgs e) { DialogResult dr = MessageBox.Show( this, string.Format("Confirmez-vous la suppression de l'exemplaire {0}?\nOuvrage de l'exemplaire : {1}.", mediabrol.Name, mediabrol.Brol.Title), "Suppression d'exemplaire", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2 ); if (DialogResult.OK == dr) { ModelAdapter.deleteMediaBrol(mediabrol); //searchControl.showSearchResultAction(sender,e); this.Focus(); } } private void saveMediabrolAction(object sender, EventArgs e) { DialogResult dr = MessageBox.Show( this, string.Format("Confirmez-vous la sauvegarde des modifications de l'exemplaire {0}?\nOuvrage de l'exemplaire : {1}.", mediabrol.Name, mediabrol.Brol.Title), "Sauvegarde d'exemplaire", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2 ); if (DialogResult.OK == dr) { setMediabrolFromForm(); ModelAdapter.updateMediaBrol(mediabrol); mediabrol = ModelAdapter.loadMediaBrol(mediabrol.Id, true); fillMediaBrolInfos(); this.Focus(); } } /// <summary> /// Open new form to edit borrow infos. /// Give the selected borrow to the form. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void borrowEditAction(object sender, EventArgs e) { //todo : fill with all infos Borrow borrow = null; try { borrow = ModelAdapter.loadBorrow((int)borrowsDGV.SelectedRows[0].Cells["id"].Value); } catch (Exception ex) { StaticObservable.notify(new Notification(Notification.VERBOSE.error, "Impossible de charger l'emprunt", ex, this)); return; } DialogResult r = editForm.ShowDialog(); //todo : prompt for plandate? and comment ? //todo : if borrower has yet other not closed borrows, send a warning if (r.Equals(DialogResult.OK)) { ModelAdapter.updateBorrow(borrow); fillBorrowsDGV(); } this.Focus(); } private void borrowRemAllAction(object sender, EventArgs e) { DialogResult r = MessageBox.Show( this, "Supprimer l'ensemble de l'historique des emprunts pour l'élément " + mediabrol.Name + "\nde [" + mediabrol.Brol.Title + "]", "Suppression de l'historique pour un élément", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (r == DialogResult.OK) { ModelAdapter.cleanBorrows(mediabrol.Id, true); fillBorrowsDGV(); } this.Focus(); } private void borrowRemSelectedAction(object sender, EventArgs e) { DialogResult r = MessageBox.Show( this, "Supprimer cet emprunt de l'historique pour l'élément " + mediabrol.Name + "\nde [" + mediabrol.Brol.Title + "]", "Suppression d'un emprunt de l'historique", MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); if (r == DialogResult.OK) { int selectedBorrowId = -1; selectedBorrowId = (int)borrowsDGV.SelectedRows[0].Cells["id"].Value; ModelAdapter.cleanBorrow(selectedBorrowId); fillBorrowsDGV(); } this.Focus(); } #endregion #region save and print DGV actions private void printDGVAction(object sender, EventArgs e) { DgvFactory.print(brolsDGV, "Exemplaires de BiblioBrol", this); } private void saveDGVAction(object sender, EventArgs e) { DgvFactory.save(brolsDGV, "Exemplaires de BiblioBrol", this); } #endregion #region IMDIChild Members public MenuStrip getMainMenuStrip() { return mainMenuStrip; } #endregion } }
Structure et Fichiers du projet
Afficher/masquer...Icône | Nom | Taille | Modification |
Icône | Nom | Taille | Modification |
| _ | Répertoire parent | 0 octets | 1732246374 22/11/2024 04:32:54 |
| _ | wizards | 0 octets | 1541007184 31/10/2018 18:33:04 |
| _ | utils | 0 octets | 1541007184 31/10/2018 18:33:04 |
| _ | controls | 0 octets | 1541007178 31/10/2018 18:32:58 |
| _ | dialogs | 0 octets | 1541007183 31/10/2018 18:33:03 |
Utilisation de l'explorateur de code
- Navigation :
- Un clic sur une icône de répertoire ouvre ce répertoire pour en afficher les fichiers.
- Lorsque le répertoire en cours ne contient pas de sous-répertoires il est possible de remonter vers le répertoire parent.
- La structure de répertoires en treetable (tableau en forme d'arborescence) n'est plus possibledans cette version.
- Un clic sur une icône de fichier ouvre ce fichier pour en afficher le code avec la coloration syntaxique adaptée en fonction du langage principal utilisé dans le fichier.
- Affichage :
- Il est possible de trier les répertoires ou les fichiers selon certains critères (nom, taille, date).
- Actions :
- Les actions possible sur les fichiers dépendent de vos droits d'utilisateur sur le site. Veuillez activer le mode utilisateur pour activer les actions.
Version en cache
22/11/2024 04:32:54 Cette version de la page est en cache (à la date du 22/11/2024 04:32:54) afin d'accélérer le traitement. Vous pouvez activer le mode utilisateur dans le menu en haut pour afficher la dernère version de la page.Document créé le 16/10/2009, dernière modification le 26/10/2018
Source du document imprimé : https://www.gaudry.be/cs-bibliobrol-source-rf-view/MediabrolForm.cs.html
L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.