[{"data":1,"prerenderedAt":6974},["ShallowReactive",2],{"post-email2trac":3,"related-email2trac":734},{"id":4,"title":5,"author":6,"body":7,"category":708,"categorySlug":709,"date":710,"description":711,"excerpt":712,"extension":724,"location":725,"meta":726,"navigation":267,"path":727,"published":267,"seo":728,"slug":474,"stem":729,"tags":730,"timeToRead":246,"__hash__":733},"posts\u002Fposts\u002FLogiciels\u002F2008-10-22-email2trac.md","Email2Trac","Ulrich Vandenhekke",{"type":8,"value":9,"toc":701},"minimark",[10,21,24,27,46,56,61,72,77,84,127,131,150,187,199,203,222,301,309,313,316,334,345,349,367,371,386,394,399,405,412,429,435,455,476,480,486,507,514,520,532,557,572,578,584,603,612,616,619,638,641,657,697],[11,12,13,16,17,20],"p",{},[14,15,5],"em",{}," est un logiciel permettant de créer un ticket sous ",[14,18,19],{},"Trac","\nen envoyant un mail au système de suivi de ticket.",[11,22,23],{},"L'intérêt d'envoyer un mail au système de suivi plutôt que de se\nconnecter sur le site Internet pour faire ses rapports d'anomalie, est\nde faciliter l'accès à de nouvelles personnes.En effet, il est parfois\nplus facile d'envoyer un mail avec son client mail préféré (en\nconsidérant que le logiciel est quasiment toujours ouvert, ce qui doit\nêtre le cas en entreprise) que d'ouvrir le navigateur pour envoyer une\nanomalie de temps en temps (ouvrir le site, se logger, remplir tous les\nchamps, ...).",[11,25,26],{},"Ceci peut également être utilisé par un outil de rapport de bug,\nenvoyant un mail automatique (avec confirmation de l'utilisateur) au\ncrash dudit logiciel.",[11,28,29,30,33,45],{},"Comme dit précédemment, ",[14,31,32],{},"EMail2Trac",[34,35,36],"sup",{},[37,38,44],"a",{"href":39,"ariaDescribedBy":40,"dataFootnoteRef":42,"id":43},"#user-content-fn-1",[41],"footnote-label","","user-content-fnref-1","1"," permet",[47,48,49,53],"ul",{},[50,51,52],"li",{},"d'ajouter un ticket en envoyant un mail, avec des fichiers attachés",[50,54,55],{},"mettre à jour un ticket (en répondant au mail du ticket par exemple)",[57,58,60],"h3",{"id":59},"installation","Installation",[11,62,63,64,66,67,71],{},"Pour installer ",[14,65,5],{},", il faut télécharger le ",[37,68,70],{"href":69},"ftp:\u002F\u002Fftp.sara.nl\u002Fpub\u002Foutgoing\u002Femail2trac.tar.gz","source"," et le\ndécompresser dans un dossier temporaire (ou utilisateur).",[73,74,76],"h4",{"id":75},"sans-gestionnaire-de-paquet","Sans gestionnaire de paquet",[11,78,79,80],{},"Ensuite la compilation se fait à l'aide des habituelles ",[81,82,83],"code",{},"autotools",[85,86,90],"pre",{"className":87,"code":88,"language":89,"meta":42,"style":42},"language-bash shiki shiki-themes one-dark-pro","configure --prefix=\u002Fusr\u002Flocal --with-mta_user=nobody --with-trac_user=www-data\nmake\nmake install\n","bash",[81,91,92,111,117],{"__ignoreMap":42},[93,94,97,101,105,108],"span",{"class":95,"line":96},"line",1,[93,98,100],{"class":99},"sVbv2","configure",[93,102,104],{"class":103},"sVC51"," --prefix=\u002Fusr\u002Flocal",[93,106,107],{"class":103}," --with-mta_user=nobody",[93,109,110],{"class":103}," --with-trac_user=www-data\n",[93,112,114],{"class":95,"line":113},2,[93,115,116],{"class":99},"make\n",[93,118,120,123],{"class":95,"line":119},3,[93,121,122],{"class":99},"make",[93,124,126],{"class":125},"subq3"," install\n",[73,128,130],{"id":129},"sous-debian","Sous Debian",[11,132,133,134,137,138,141,142,145,146,149],{},"Après avoir installé les paquets ",[81,135,136],{},"cdbs"," et ",[81,139,140],{},"autotools-dev",", vous pouvez\nexécuter les commandes suivante en ",[14,143,144],{},"root"," (ou la première à l'aide de\n",[14,147,148],{},"fakeroot",") :",[85,151,153],{"className":87,"code":152,"language":89,"meta":42,"style":42},"debian\u002Frules binary\ndpkg -i ..\u002Femail2trac_\u003Cversion>.deb\n",[81,154,155,163],{"__ignoreMap":42},[93,156,157,160],{"class":95,"line":96},[93,158,159],{"class":99},"debian\u002Frules",[93,161,162],{"class":125}," binary\n",[93,164,165,168,171,174,178,181,184],{"class":95,"line":113},[93,166,167],{"class":99},"dpkg",[93,169,170],{"class":103}," -i",[93,172,173],{"class":125}," ..\u002Femail2trac_",[93,175,177],{"class":176},"sn6KH","\u003C",[93,179,180],{"class":125},"versio",[93,182,183],{"class":176},"n>",[93,185,186],{"class":125},".deb\n",[11,188,189,190,193,194],{},"Pour le gestionnaire de paquets ",[14,191,192],{},"RPM"," vous pouvez visiter la page\n",[37,195,196],{"href":196,"rel":197},"https:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki\u002FEmail2tracInstallation",[198],"nofollow",[57,200,202],{"id":201},"configuration-de-lapplication","Configuration de l'application",[11,204,205,206,209,210,218,219,221],{},"La deuxième étape est l'édition du fichier ",[81,207,208],{},"\u002Fetc\u002Femail2trac.conf",". Dans\nce fichier les informations à renseigner concerne la gestion du projet\nTrac",[34,211,212],{},[37,213,217],{"href":214,"ariaDescribedBy":215,"dataFootnoteRef":42,"id":216},"#user-content-fn-2",[41],"user-content-fnref-2","2"," par ",[14,220,5],{},".",[85,223,227],{"className":224,"code":225,"language":226,"meta":42,"style":42},"language-ini shiki shiki-themes one-dark-pro","[DEFAULT]\nproject: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet1\nspam_level: 5\ndrop_spam: 1\nticket_update: 1\nstrip_signature: 1\n\n[projet2]\nproject: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet2\nspam_level: 5\ndrop_spam: 1\nticket_update: 1\nstrip_signature: 1\n","ini",[81,228,229,234,239,244,250,256,262,269,275,281,286,291,296],{"__ignoreMap":42},[93,230,231],{"class":95,"line":96},[93,232,233],{"class":99},"[DEFAULT]\n",[93,235,236],{"class":95,"line":113},[93,237,238],{"class":125},"project: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet1\n",[93,240,241],{"class":95,"line":119},[93,242,243],{"class":125},"spam_level: 5\n",[93,245,247],{"class":95,"line":246},4,[93,248,249],{"class":125},"drop_spam: 1\n",[93,251,253],{"class":95,"line":252},5,[93,254,255],{"class":125},"ticket_update: 1\n",[93,257,259],{"class":95,"line":258},6,[93,260,261],{"class":125},"strip_signature: 1\n",[93,263,265],{"class":95,"line":264},7,[93,266,268],{"emptyLinePlaceholder":267},true,"\n",[93,270,272],{"class":95,"line":271},8,[93,273,274],{"class":99},"[projet2]\n",[93,276,278],{"class":95,"line":277},9,[93,279,280],{"class":125},"project: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet2\n",[93,282,284],{"class":95,"line":283},10,[93,285,243],{"class":125},[93,287,289],{"class":95,"line":288},11,[93,290,249],{"class":125},[93,292,294],{"class":95,"line":293},12,[93,295,255],{"class":125},[93,297,299],{"class":95,"line":298},13,[93,300,261],{"class":125},[11,302,303,304,221],{},"Les différentes options dépendent de ce que vous souhaitez avoir. Les\ndifférentes possibilités sont à l'adresse ",[37,305,308],{"href":306,"rel":307},"https:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki\u002FEmail2tracConfiguration",[198],"suivante",[73,310,312],{"id":311},"spam","Spam",[11,314,315],{},"La gestion du SPAM se fait à l'aide des 3 lignes :",[85,317,319],{"className":224,"code":318,"language":226,"meta":42,"style":42},"spam_level: 5\ndrop_spam: 1\nspam_header: X-Spam-Level\n",[81,320,321,325,329],{"__ignoreMap":42},[93,322,323],{"class":95,"line":96},[93,324,243],{"class":125},[93,326,327],{"class":95,"line":113},[93,328,249],{"class":125},[93,330,331],{"class":95,"line":119},[93,332,333],{"class":125},"spam_header: X-Spam-Level\n",[11,335,336,337,340,341,344],{},"La ligne ",[81,338,339],{},"spam_header"," étant à configurer suivant votre politique de\ngestion des spams (utilisation de ",[14,342,343],{},"SpamAssassin"," ou d'un autre\nutilitaire).",[57,346,348],{"id":347},"configuration-de-postfix","Configuration de Postfix",[11,350,351,352,354,355,357,358,361,362],{},"Il faut maintenant configurer notre système de messagerie pour intégrer\n",[14,353,5],{},". Nous allons détailler ici la mise en place de ",[14,356,5],{},"\nsous ",[14,359,360],{},"Postfix",". Pour les autres serveurs mails vous pouvez visiter la\npage : ",[37,363,366],{"href":364,"rel":365},"http:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki",[198],"http:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki\u002FEmail2tracMta",[73,368,370],{"id":369},"aliases","Aliases",[11,372,373,374,377,378,381,382,385],{},"La première méthode concerne la modification du fichier des ",[14,375,376],{},"alias"," pour\nune configuration standard de ",[14,379,380],{},"postfix",". Nous allons commencer par\nmodifier le fichier ",[81,383,384],{},"\u002Fetc\u002Faliases"," en ajoutant la ligne suivante (pour\nle projet 1) :",[85,387,392],{"className":388,"code":390,"language":391},[389],"language-text","projet1: |\"\u002Fusr\u002Fbin\u002Frun_email2trac\"\n","text",[81,393,390],{"__ignoreMap":42},[11,395,396,397,149],{},"Il est également possible de préciser le projet sur lequel il faut\ntravailler (projet définit dans ",[81,398,208],{},[85,400,403],{"className":401,"code":402,"language":391},[389],"projet2: |\"\u002Fusr\u002Fbin\u002Frun_email2trac --project=projet2 --ticket_prefix=devel\"\n",[81,404,402],{"__ignoreMap":42},[11,406,407,408,411],{},"Dans le fichier ",[81,409,410],{},"\u002Fetc\u002Fpostfix\u002Fmain.cf",", il faut avoir défini\nl'emplacement des alias de la manière suivante :",[85,413,415],{"className":224,"code":414,"language":226,"meta":42,"style":42},"alias_maps = hash:\u002Fetc\u002Faliases\n",[81,416,417],{"__ignoreMap":42},[93,418,419,423,426],{"class":95,"line":96},[93,420,422],{"class":421},"seHd6","alias_maps",[93,424,425],{"class":176}," =",[93,427,428],{"class":125}," hash:\u002Fetc\u002Faliases\n",[11,430,431,432,434],{},"Les commandes suivantes permettent de mettre à jour le fichier des alias\net demander à ",[14,433,360],{}," de recharger la configuration :",[85,436,438],{"className":87,"code":437,"language":89,"meta":42,"style":42},"postaliases \u002Fetc\u002Faliases\npostfix reload\n",[81,439,440,448],{"__ignoreMap":42},[93,441,442,445],{"class":95,"line":96},[93,443,444],{"class":99},"postaliases",[93,446,447],{"class":125}," \u002Fetc\u002Faliases\n",[93,449,450,452],{"class":95,"line":113},[93,451,380],{"class":99},[93,453,454],{"class":125}," reload\n",[11,456,457,458,464,465,471,472,475],{},"Une fois ceci terminé, les mails envoyé à ",[14,459,460],{},[37,461,463],{"href":462},"mailto:projet1@domain.org","projet1@domain.org"," et\n",[14,466,467],{},[37,468,470],{"href":469},"mailto:projet2@domaine.org","projet2@domaine.org"," seront automatiquement redirigés vers ",[14,473,474],{},"email2trac","\nqui s'occupera de créer le ticket dans votre système de suivi.",[73,477,479],{"id":478},"transport","Transport",[11,481,482,483,485],{},"Cette configuration concerne les personnes qui ont une configuration\nplus complexe de ",[14,484,380],{}," par exemple en gérant des domaines virtuels.\nIl est alors possible de définir une redirection des tickets pour un\nsous domaine.",[11,487,488,489,492,493,499,500,506],{},"Nous avons donc créé le sous-domaine ",[14,490,491],{},"tickets.domaine.org"," et voulons\nque les mails envoyés à ",[14,494,495],{},[37,496,498],{"href":497},"mailto:projet1@tickets.domaine.org","projet1@tickets.domaine.org"," est\n",[14,501,502],{},[37,503,505],{"href":504},"mailto:projet2@tickets.domaine.org","projet2@tickets.domaine.org"," soient redirigés vers email2trac.",[11,508,509,510,513],{},"Pour cela nous allons créer un fichier ",[81,511,512],{},"\u002Fetc\u002Fpostfix\u002Ftransport"," avec les\nlignes ci-dessous :",[85,515,518],{"className":516,"code":517,"language":391},[389],"projet1@tickets.domaine.org  projet1:\nprojet2@tickets.domaine.org  projet2:\n",[81,519,517],{"__ignoreMap":42},[11,521,522,523,525,526,528,529,221],{},"Puis dans le fichier ",[81,524,410],{}," nous allons définir que pour\nle sous domaine ",[14,527,491],{},", il faut lire et utiliser le\nfichier des transports. (Attention il ne faut pas ajouter ce\nsous-domaine dans ",[81,530,531],{},"mydestination",[85,533,535],{"className":224,"code":534,"language":226,"meta":42,"style":42},"relay_domains = tickets.domaine.org\ntransport_maps = hash:\u002Fetc\u002Fpostfix\u002Ftransport\n",[81,536,537,547],{"__ignoreMap":42},[93,538,539,542,544],{"class":95,"line":96},[93,540,541],{"class":421},"relay_domains",[93,543,425],{"class":176},[93,545,546],{"class":125}," tickets.domaine.org\n",[93,548,549,552,554],{"class":95,"line":113},[93,550,551],{"class":421},"transport_maps",[93,553,425],{"class":176},[93,555,556],{"class":125}," hash:\u002Fetc\u002Fpostfix\u002Ftransport\n",[11,558,559,560,563,564,567,568,571],{},"Ensuite, il nous faut définir dans le fichier ",[81,561,562],{},"\u002Fetc\u002Fpostfix\u002Fmaster.cf","\nnotre nouveau mode de transport. Il n'est pas obligé d'utiliser\n",[81,565,566],{},"run_email2trac"," qui fait un changement d'utilisateur, car dans le\nfichier ",[81,569,570],{},"master.cf"," on peut directement définir l'utilisateur de\ndestination.",[85,573,576],{"className":574,"code":575,"language":391},[389],"projet1      unix  -       n       n       -       -       pipe\n    flags=FR user=www-data argv=\u002Fusr\u002Fbin\u002Femail2trac\n    ${nexthop} ${user}\nprojet2      unix  -       n       n       -       -       pipe\n    flags=FR user=www-data argv=\u002Fusr\u002Fbin\u002Femail2trac\n    ${nexthop} ${user}\\r\n",[81,577,575],{"__ignoreMap":42},[11,579,580,581,583],{},"Enfin il faut redémarrer ",[14,582,380],{}," :",[85,585,587],{"className":87,"code":586,"language":89,"meta":42,"style":42},"postmap \u002Fetc\u002Fpostfix\u002Ftransport\npostfix reload\n",[81,588,589,597],{"__ignoreMap":42},[93,590,591,594],{"class":95,"line":96},[93,592,593],{"class":99},"postmap",[93,595,596],{"class":125}," \u002Fetc\u002Fpostfix\u002Ftransport\n",[93,598,599,601],{"class":95,"line":113},[93,600,380],{"class":99},[93,602,454],{"class":125},[11,604,605,606,221],{},"Et voilà vous pouvez envoyer vos mails à\n",[14,607,608],{},[37,609,611],{"href":610},"mailto:projet1@tickets.shadoware.org","projet1@tickets.shadoware.org",[57,613,615],{"id":614},"tester","Tester",[11,617,618],{},"Pour tester il est possible d'envoyer un mail, comme ceci :",[85,620,622],{"className":87,"code":621,"language":89,"meta":42,"style":42},"mail projet1@tickets.shadoware.org \u003C ticket.txt\n",[81,623,624],{"__ignoreMap":42},[93,625,626,629,632,635],{"class":95,"line":96},[93,627,628],{"class":99},"mail",[93,630,631],{"class":125}," projet1@tickets.shadoware.org",[93,633,634],{"class":176}," \u003C ",[93,636,637],{"class":125},"ticket.txt\n",[11,639,640],{},"En cas de problème, vous pouvez vérifier que email2trac est bien\nconfiguré avec la commande :",[85,642,644],{"className":87,"code":643,"language":89,"meta":42,"style":42},"email2trac --project=projet1 \u003C ticket.txt\n",[81,645,646],{"__ignoreMap":42},[93,647,648,650,653,655],{"class":95,"line":96},[93,649,474],{"class":99},[93,651,652],{"class":103}," --project=projet1",[93,654,634],{"class":176},[93,656,637],{"class":125},[658,659,662,668],"section",{"className":660,"dataFootnotes":42},[661],"footnotes",[663,664,667],"h2",{"className":665,"id":41},[666],"sr-only","Footnotes",[669,670,671,685],"ol",{},[50,672,674,677,678],{"id":673},"user-content-fn-1",[37,675,366],{"href":364,"rel":676},[198]," ",[37,679,684],{"href":680,"ariaLabel":681,"className":682,"dataFootnoteBackref":42},"#user-content-fnref-1","Back to reference 1",[683],"data-footnote-backref","↩",[50,686,688,677,692],{"id":687},"user-content-fn-2",[37,689,690],{"href":690,"rel":691},"http:\u002F\u002Ftrac.edgewall.org\u002F",[198],[37,693,684],{"href":694,"ariaLabel":695,"className":696,"dataFootnoteBackref":42},"#user-content-fnref-2","Back to reference 2",[683],[698,699,700],"style",{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}",{"title":42,"searchDepth":113,"depth":113,"links":702},[703,704,705,706,707],{"id":59,"depth":119,"text":60},{"id":201,"depth":119,"text":202},{"id":347,"depth":119,"text":348},{"id":614,"depth":119,"text":615},{"id":41,"depth":113,"text":667},"Logiciels","logiciels","2008-10-22","Email2Trac est un logiciel permettant de créer un ticket sous Trac\nen envoyant un mail au système de suivi de ticket.",{"type":8,"value":713},[714,720,722],[11,715,716,16,718,20],{},[14,717,5],{},[14,719,19],{},[11,721,23],{},[11,723,26],{},"md","Lille, France",{},"\u002Fpost\u002Femail2trac",{"title":5,"description":711},"posts\u002FLogiciels\u002F2008-10-22-email2trac",[731,732],"debian","dedie","llLWYwDr2WQzswEZDA9vDYeLWcGqHIhabMKl6VUmCmo",[735,2280,2364,2813,4439,5980],{"id":736,"title":737,"author":6,"body":738,"category":2246,"categorySlug":2247,"date":2248,"description":42,"excerpt":2249,"extension":724,"location":725,"meta":2270,"navigation":267,"path":2271,"published":267,"seo":2272,"slug":2273,"stem":2274,"tags":2275,"timeToRead":293,"__hash__":2279},"posts\u002Fposts\u002FProgrammation\u002F2016-12-10-findsimilarity.md","FindSimilarity - Trouver les différences entre plusieurs vidéos",{"type":8,"value":739,"toc":2234},[740,744,747,750,753,756,761,768,772,775,778,810,813,863,867,870,884,890,898,901,910,913,916,919,1347,1351,1354,1363,1366,1369,1395,1398,1522,1525,1531,1546,1549,1552,2102,2112,2116,2119,2123,2126,2129,2136,2139,2143,2149,2159,2162,2179,2196,2200,2203,2209,2212,2218,2224,2228,2231],[57,741,743],{"id":742},"introduction","Introduction",[11,745,746],{},"Bonjour,",[11,748,749],{},"Je souhaite vous présenter une petite expérience que je viens d'écrire.",[11,751,752],{},"Cela fait plusieurs années que je souhaitais m'amuser sur la librairie OpenCV mais sans jamais en avoir eu l'utilité. J'ai profité d'avoir un peu de temps libre, pour écrire un petit\nprogramme dont le but est de comparer un ensemble de vidéos.",[11,754,755],{},"Le but est ensuite de dire si dans cet ensemble de vidéos, deux vidéos sont identiques, ou se ressemblent, ou sont trop éloignées.",[11,757,758],{},[14,759,760],{},"J'ai souhaité faire cette expérience par amusement, je n'ai donc pas passé beaucoup de temps sur la qualité du code écrit. Ce dernier aurait pu être mieux découpé, posséder des\ncommentaires, des tests unitaires, ... . Si vous voulez utiliser ce code pour un véritable usage production, n'hésitez pas à améliorer celui ci.",[11,762,763,764,221],{},"Vous pouvez trouver le code source de cette expérience à l'adresse suivante : ",[37,765,766],{"href":766,"rel":767},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Ffind-similarity",[198],[57,769,771],{"id":770},"le-jeux-de-données","Le jeux de données",[11,773,774],{},"J'ai pris plusieurs films en DVD que je possède. Possédant un NAS, et une chromecast, j'encode ces DVD au format vidéo et je les y dépose. Malheureusement la qualité est dégradée\npar rapport au DVD.",[11,776,777],{},"Pour constituer le jeu de données, je prends ces films encodés, que je dépose dans un dossier. Je copie certains d'entre eux telquel",[85,779,781],{"className":87,"code":780,"language":89,"meta":42,"style":42},"mkdir example\ncd example\ncp ..\u002Ffilm1.avi film1_copy.avi\n",[81,782,783,791,799],{"__ignoreMap":42},[93,784,785,788],{"class":95,"line":96},[93,786,787],{"class":99},"mkdir",[93,789,790],{"class":125}," example\n",[93,792,793,797],{"class":95,"line":113},[93,794,796],{"class":795},"sjrmR","cd",[93,798,790],{"class":125},[93,800,801,804,807],{"class":95,"line":119},[93,802,803],{"class":99},"cp",[93,805,806],{"class":125}," ..\u002Ffilm1.avi",[93,808,809],{"class":125}," film1_copy.avi\n",[11,811,812],{},"J'encode certains de ces films avec une résolution différente :",[85,814,816],{"className":87,"code":815,"language":89,"meta":42,"style":42},"avconv -i film2.m4v  -preset veryslow -s 320x240 film2.320x240.m4v\navconv -i film2.m4v  -preset veryslow -s 640x480 film2.640x480.m4v\n",[81,817,818,843],{"__ignoreMap":42},[93,819,820,823,825,828,831,834,837,840],{"class":95,"line":96},[93,821,822],{"class":99},"avconv",[93,824,170],{"class":103},[93,826,827],{"class":125}," film2.m4v",[93,829,830],{"class":103},"  -preset",[93,832,833],{"class":125}," veryslow",[93,835,836],{"class":103}," -s",[93,838,839],{"class":125}," 320x240",[93,841,842],{"class":125}," film2.320x240.m4v\n",[93,844,845,847,849,851,853,855,857,860],{"class":95,"line":113},[93,846,822],{"class":99},[93,848,170],{"class":103},[93,850,827],{"class":125},[93,852,830],{"class":103},[93,854,833],{"class":125},[93,856,836],{"class":103},[93,858,859],{"class":125}," 640x480",[93,861,862],{"class":125}," film2.640x480.m4v\n",[57,864,866],{"id":865},"comment-fonctionne-la-comparaison","Comment fonctionne la comparaison",[11,868,869],{},"Avant de parler de la comparaison, parlons des fichiers que nous allons comparer. Le programme va se constituer une liste des fichiers à comparer et pour chaque fichier va lire\nles informations suivantes :",[47,871,872,875,878,881],{},[50,873,874],{},"la durée du film",[50,876,877],{},"la largeur",[50,879,880],{},"la hauteur",[50,882,883],{},"une miniature du film (utilisée pour comparer à l'oeil les vidéos)",[11,885,886,887,221],{},"Ensuite, une fois les méta-données récupérées, le programe se constitue une liste de paires de fichiers à comparer en sélectionnant les fichiers qui ont une durée identique à +\u002F- 5\nsecondes. Ce paramètre est modifiable au niveau de la constante ",[81,888,889],{},"DELTA_SEC",[11,891,892,893,221],{},"Enfin vient la comparaison pour laquelle je me suis simplement basé sur les exemples du site OpenCV que vous pouvez trouver dans la rubrique\n",[37,894,897],{"href":895,"rel":896},"http:\u002F\u002Fdocs.opencv.org\u002F2.4\u002Fdoc\u002Ftutorials\u002Fhighgui\u002Fvideo-input-psnr-ssim\u002Fvideo-input-psnr-ssim.html",[198],"Video Input with OpenCV and similarity measurement",[11,899,900],{},"J'ai utilisé l'algorithme PSNR (Peak signal-to-noise ratio) pour déterminer si les deux images de la vidéos sont plutôt proches ou éloignées. Cet algorithme calcul la distorsion\nentre deux images. Il est principalement utilisé pour quantifier la performance réalisée par un encodeur lors de la compression d'une vidéo. Une valeur entre 30 et 50 signifie que\nles images sont relativement proches. Plus la valeur est haute, et plus la qualité d'image est conservée entre les deux images. Si la valeur est inférieure à 30 on peut estimer qu'il\ny a une forte chance pour que les images soit différentes.",[11,902,903,904,909],{},"Vous pouvez retrouver les formules utilisées par le calcul sur le site d'OpenCV ou sur la page ",[37,905,908],{"href":906,"rel":907},"https:\u002F\u002Ffr.wikipedia.org\u002Fwiki\u002FPeak_Signal_to_Noise_Ratio",[198],"Wikipedia",". Est-ce que ce\ncalcul est le meilleur pour trouver les images similaires ? Je ne sais pas. Si vous avez d'autres propositions, on peut les tester.",[11,911,912],{},"Sur une vidéo on a une multitude d'images (sur un film d'une heure et demie à 25 images secondes, nous en avons 135 000), on pourrait comparer chaque image de la vidéo pour se faire\nune moyenne, de mon coté j'ai préféré comparer une image au milieu de la vidéo afin de parcourir plus vite les vidéos.",[11,914,915],{},"De la même manière pour m'abstraire de la taille de la vidéo qui peut avoir été modififée, je redimensionne, à tort ou à raison, les deux images à une taille identique\n(arbitrairement: 160x120).",[11,917,918],{},"Je vous présente donc ci-dessous l'algorithme que vous pouvez retrouver sur le site d'OpenCV. J'ai légèrement modifié l'algorithme pour redimensionner les images ainsi que pour\nretourner une valeur de PSNR infiniment grande quand deux vidéos sont identiques.",[85,920,924],{"className":921,"code":922,"language":923,"meta":42,"style":42},"language-cpp shiki shiki-themes one-dark-pro","double getPSNR(const cv::Mat& F1, const cv::Mat& F2) {\n    cv::Mat I1, I2;\n\n    cv::resize(F1, I1, cv::Size(160, 120));\n    cv::resize(F2, I2, cv::Size(160, 120));\n\n    cv::Mat s1;\n    cv::absdiff(I1, I2, s1);   \u002F\u002F |I1 - I2|\n    s1.convertTo(s1, CV_32F);  \u002F\u002F cannot make a square on 8 bits\n    s1 = s1.mul(s1);           \u002F\u002F |I1 - I2|^2\n\n    cv::Scalar s = sum(s1);    \u002F\u002F sum elements per channel\n\n    double sse = s.val[0] + s.val[1] + s.val[2]; \u002F\u002F sum channels\n\n    if( sse \u003C= 1e-10) {        \u002F\u002F for small values return zero\n        return std::numeric_limits\u003Cdouble>::infinity();\n    } else {\n        double mse  = sse \u002F (double)(I1.channels() * I1.total());\n        double psnr = 10.0 * log10((255 * 255) \u002F mse);\n        return psnr;\n    }\n","cpp",[81,925,926,971,976,980,1007,1028,1032,1037,1051,1067,1089,1093,1108,1112,1174,1179,1209,1234,1246,1295,1333,1341],{"__ignoreMap":42},[93,927,928,931,934,937,940,943,947,950,954,957,959,961,963,965,968],{"class":95,"line":96},[93,929,930],{"class":421},"double",[93,932,933],{"class":99}," getPSNR",[93,935,936],{"class":176},"(",[93,938,939],{"class":421},"const",[93,941,942],{"class":176}," cv::",[93,944,946],{"class":945},"sU0A5","Mat",[93,948,949],{"class":421},"&",[93,951,953],{"class":952},"s_ZVi"," F1",[93,955,956],{"class":176},", ",[93,958,939],{"class":421},[93,960,942],{"class":176},[93,962,946],{"class":945},[93,964,949],{"class":421},[93,966,967],{"class":952}," F2",[93,969,970],{"class":176},") {\n",[93,972,973],{"class":95,"line":113},[93,974,975],{"class":176},"    cv::Mat I1, I2;\n",[93,977,978],{"class":95,"line":119},[93,979,268],{"emptyLinePlaceholder":267},[93,981,982,985,988,991,994,996,999,1001,1004],{"class":95,"line":246},[93,983,984],{"class":176},"    cv::",[93,986,987],{"class":99},"resize",[93,989,990],{"class":176},"(F1, I1, cv::",[93,992,993],{"class":99},"Size",[93,995,936],{"class":176},[93,997,998],{"class":103},"160",[93,1000,956],{"class":176},[93,1002,1003],{"class":103},"120",[93,1005,1006],{"class":176},"));\n",[93,1008,1009,1011,1013,1016,1018,1020,1022,1024,1026],{"class":95,"line":252},[93,1010,984],{"class":176},[93,1012,987],{"class":99},[93,1014,1015],{"class":176},"(F2, I2, cv::",[93,1017,993],{"class":99},[93,1019,936],{"class":176},[93,1021,998],{"class":103},[93,1023,956],{"class":176},[93,1025,1003],{"class":103},[93,1027,1006],{"class":176},[93,1029,1030],{"class":95,"line":258},[93,1031,268],{"emptyLinePlaceholder":267},[93,1033,1034],{"class":95,"line":264},[93,1035,1036],{"class":176},"    cv::Mat s1;\n",[93,1038,1039,1041,1044,1047],{"class":95,"line":271},[93,1040,984],{"class":176},[93,1042,1043],{"class":99},"absdiff",[93,1045,1046],{"class":176},"(I1, I2, s1);",[93,1048,1050],{"class":1049},"sV9Aq","   \u002F\u002F |I1 - I2|\n",[93,1052,1053,1056,1058,1061,1064],{"class":95,"line":277},[93,1054,1055],{"class":945},"    s1",[93,1057,221],{"class":176},[93,1059,1060],{"class":99},"convertTo",[93,1062,1063],{"class":176},"(s1, CV_32F);",[93,1065,1066],{"class":1049},"  \u002F\u002F cannot make a square on 8 bits\n",[93,1068,1069,1072,1075,1078,1080,1083,1086],{"class":95,"line":283},[93,1070,1071],{"class":176},"    s1 ",[93,1073,1074],{"class":421},"=",[93,1076,1077],{"class":945}," s1",[93,1079,221],{"class":176},[93,1081,1082],{"class":99},"mul",[93,1084,1085],{"class":176},"(s1);",[93,1087,1088],{"class":1049},"           \u002F\u002F |I1 - I2|^2\n",[93,1090,1091],{"class":95,"line":288},[93,1092,268],{"emptyLinePlaceholder":267},[93,1094,1095,1098,1100,1103,1105],{"class":95,"line":293},[93,1096,1097],{"class":176},"    cv::Scalar s ",[93,1099,1074],{"class":421},[93,1101,1102],{"class":99}," sum",[93,1104,1085],{"class":176},[93,1106,1107],{"class":1049},"    \u002F\u002F sum elements per channel\n",[93,1109,1110],{"class":95,"line":298},[93,1111,268],{"emptyLinePlaceholder":267},[93,1113,1115,1118,1121,1123,1126,1128,1132,1135,1138,1141,1144,1146,1148,1150,1152,1154,1156,1158,1160,1162,1164,1166,1168,1171],{"class":95,"line":1114},14,[93,1116,1117],{"class":421},"    double",[93,1119,1120],{"class":176}," sse ",[93,1122,1074],{"class":421},[93,1124,1125],{"class":945}," s",[93,1127,221],{"class":176},[93,1129,1131],{"class":1130},"sVyAn","val",[93,1133,1134],{"class":176},"[",[93,1136,1137],{"class":103},"0",[93,1139,1140],{"class":176},"] ",[93,1142,1143],{"class":421},"+",[93,1145,1125],{"class":945},[93,1147,221],{"class":176},[93,1149,1131],{"class":1130},[93,1151,1134],{"class":176},[93,1153,44],{"class":103},[93,1155,1140],{"class":176},[93,1157,1143],{"class":421},[93,1159,1125],{"class":945},[93,1161,221],{"class":176},[93,1163,1131],{"class":1130},[93,1165,1134],{"class":176},[93,1167,217],{"class":103},[93,1169,1170],{"class":176},"];",[93,1172,1173],{"class":1049}," \u002F\u002F sum channels\n",[93,1175,1177],{"class":95,"line":1176},15,[93,1178,268],{"emptyLinePlaceholder":267},[93,1180,1182,1185,1188,1191,1194,1197,1200,1203,1206],{"class":95,"line":1181},16,[93,1183,1184],{"class":421},"    if",[93,1186,1187],{"class":176},"( sse ",[93,1189,1190],{"class":421},"\u003C=",[93,1192,1193],{"class":103}," 1",[93,1195,1196],{"class":1130},"e",[93,1198,1199],{"class":176},"-",[93,1201,1202],{"class":103},"10",[93,1204,1205],{"class":176},") {",[93,1207,1208],{"class":1049},"        \u002F\u002F for small values return zero\n",[93,1210,1212,1215,1218,1221,1223,1225,1228,1231],{"class":95,"line":1211},17,[93,1213,1214],{"class":421},"        return",[93,1216,1217],{"class":176}," std::",[93,1219,1220],{"class":945},"numeric_limits",[93,1222,177],{"class":176},[93,1224,930],{"class":421},[93,1226,1227],{"class":176},">::",[93,1229,1230],{"class":99},"infinity",[93,1232,1233],{"class":176},"();\n",[93,1235,1237,1240,1243],{"class":95,"line":1236},18,[93,1238,1239],{"class":176},"    } ",[93,1241,1242],{"class":421},"else",[93,1244,1245],{"class":176}," {\n",[93,1247,1249,1252,1255,1257,1259,1262,1265,1267,1270,1273,1275,1278,1281,1284,1287,1289,1292],{"class":95,"line":1248},19,[93,1250,1251],{"class":421},"        double",[93,1253,1254],{"class":176}," mse  ",[93,1256,1074],{"class":421},[93,1258,1120],{"class":176},[93,1260,1261],{"class":421},"\u002F",[93,1263,1264],{"class":176}," (",[93,1266,930],{"class":421},[93,1268,1269],{"class":176},")(",[93,1271,1272],{"class":945},"I1",[93,1274,221],{"class":176},[93,1276,1277],{"class":99},"channels",[93,1279,1280],{"class":176},"() ",[93,1282,1283],{"class":421},"*",[93,1285,1286],{"class":945}," I1",[93,1288,221],{"class":176},[93,1290,1291],{"class":99},"total",[93,1293,1294],{"class":176},"());\n",[93,1296,1298,1300,1303,1305,1308,1311,1314,1317,1320,1322,1325,1328,1330],{"class":95,"line":1297},20,[93,1299,1251],{"class":421},[93,1301,1302],{"class":176}," psnr ",[93,1304,1074],{"class":421},[93,1306,1307],{"class":103}," 10.0",[93,1309,1310],{"class":421}," *",[93,1312,1313],{"class":99}," log10",[93,1315,1316],{"class":176},"((",[93,1318,1319],{"class":103},"255",[93,1321,1310],{"class":421},[93,1323,1324],{"class":103}," 255",[93,1326,1327],{"class":176},") ",[93,1329,1261],{"class":421},[93,1331,1332],{"class":176}," mse);\n",[93,1334,1336,1338],{"class":95,"line":1335},21,[93,1337,1214],{"class":421},[93,1339,1340],{"class":176}," psnr;\n",[93,1342,1344],{"class":95,"line":1343},22,[93,1345,1346],{"class":176},"    }\n",[57,1348,1350],{"id":1349},"optimisation","Optimisation",[11,1352,1353],{},"La raison qui fait que je voulais m'amuser avec OpenCV c'est qu'il permet de faire ces calculs à l'aide du GPU au lieu du CPU.",[11,1355,1356,1357,1362],{},"L'utilisation du GPU permet d'améliorer la vitesse de calcul pour tout ce qui est traitement d'image, ce pour quoi un GPU est prévu pour. Pour plus d'informations sur\nl'utilisation du GPU dans OpenCV peut être trouvé sur la page ",[37,1358,1361],{"href":1359,"rel":1360},"http:\u002F\u002Fopencv.org\u002Fplatforms\u002Fcuda.html",[198],"CUDA"," d'OpenCV.",[11,1364,1365],{},"Le problème est que sur la version de Debian jessie que j'utilise, OpenCV n'est pas compilé avec CUDA, et ne permet donc pas d'utiliser le GPU. J'ai donc dû compiler ma propre\nversion d'OpenCV.",[11,1367,1368],{},"Pour cela la première étape consiste à récupérer le code source et à se positionner sur la branche que l'on souhaite compiler. Pour ma part je préfère compiler sur la branche 2.4,\nplus proche de la version de Debian.",[85,1370,1372],{"className":87,"code":1371,"language":89,"meta":42,"style":42},"git clone https:\u002F\u002Fgithub.com\u002Fopencv\u002Fopencv.git\ngit checkout 2.4\n",[81,1373,1374,1385],{"__ignoreMap":42},[93,1375,1376,1379,1382],{"class":95,"line":96},[93,1377,1378],{"class":99},"git",[93,1380,1381],{"class":125}," clone",[93,1383,1384],{"class":125}," https:\u002F\u002Fgithub.com\u002Fopencv\u002Fopencv.git\n",[93,1386,1387,1389,1392],{"class":95,"line":113},[93,1388,1378],{"class":99},[93,1390,1391],{"class":125}," checkout",[93,1393,1394],{"class":103}," 2.4\n",[11,1396,1397],{},"Viens ensuite la compilation :",[85,1399,1401],{"className":87,"code":1400,"language":89,"meta":42,"style":42},"mkdir build\ncd build\ncmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=\u002Fhome\u002Fphoenix\u002Fusr\u002Flocal  -DENABLE_SSE=ON -DENABLE_SSE2=ON -DENABLE_SSE3=ON -DWITH_TBB=ON -DWITH_1394=ON -DWITH_V4L=ON -DWITH_OPENGL=ON  -DWITH_GTK=ON -DWITH_JASPER=ON -DWITH_JPEG=ON -DWITH_PNG=ON -DWITH_TIFF=ON  -DWITH_OPENEXR=ON -DWITH_PVAPI=ON   -DWITH_EIGEN=ON -DCMAKE_SKIP_RPATH=ON -D WITH_CUDA=ON -D ENABLE_FAST_MATH=1 -D CUDA_FAST_MATH=1 -D WITH_CUBLAS=1 -DWITH_IPP=ON -D CUDA_GENERATION=Auto -D WITH_FFMPEG=ON  ..\u002F\n",[81,1402,1403,1410,1416],{"__ignoreMap":42},[93,1404,1405,1407],{"class":95,"line":96},[93,1406,787],{"class":99},[93,1408,1409],{"class":125}," build\n",[93,1411,1412,1414],{"class":95,"line":113},[93,1413,796],{"class":795},[93,1415,1409],{"class":125},[93,1417,1418,1421,1424,1427,1429,1432,1435,1438,1441,1444,1447,1450,1453,1456,1459,1462,1465,1468,1471,1474,1477,1480,1482,1485,1487,1490,1492,1494,1497,1499,1501,1504,1506,1509,1511,1514,1516,1519],{"class":95,"line":119},[93,1419,1420],{"class":99},"cmake",[93,1422,1423],{"class":103}," -D",[93,1425,1426],{"class":125}," CMAKE_BUILD_TYPE=RELEASE",[93,1428,1423],{"class":103},[93,1430,1431],{"class":125}," CMAKE_INSTALL_PREFIX=\u002Fhome\u002Fphoenix\u002Fusr\u002Flocal",[93,1433,1434],{"class":103},"  -DENABLE_SSE=ON",[93,1436,1437],{"class":103}," -DENABLE_SSE2=ON",[93,1439,1440],{"class":103}," -DENABLE_SSE3=ON",[93,1442,1443],{"class":103}," -DWITH_TBB=ON",[93,1445,1446],{"class":103}," -DWITH_1394=ON",[93,1448,1449],{"class":103}," -DWITH_V4L=ON",[93,1451,1452],{"class":103}," -DWITH_OPENGL=ON",[93,1454,1455],{"class":103},"  -DWITH_GTK=ON",[93,1457,1458],{"class":103}," -DWITH_JASPER=ON",[93,1460,1461],{"class":103}," -DWITH_JPEG=ON",[93,1463,1464],{"class":103}," -DWITH_PNG=ON",[93,1466,1467],{"class":103}," -DWITH_TIFF=ON",[93,1469,1470],{"class":103},"  -DWITH_OPENEXR=ON",[93,1472,1473],{"class":103}," -DWITH_PVAPI=ON",[93,1475,1476],{"class":103},"   -DWITH_EIGEN=ON",[93,1478,1479],{"class":103}," -DCMAKE_SKIP_RPATH=ON",[93,1481,1423],{"class":103},[93,1483,1484],{"class":125}," WITH_CUDA=ON",[93,1486,1423],{"class":103},[93,1488,1489],{"class":125}," ENABLE_FAST_MATH=",[93,1491,44],{"class":103},[93,1493,1423],{"class":103},[93,1495,1496],{"class":125}," CUDA_FAST_MATH=",[93,1498,44],{"class":103},[93,1500,1423],{"class":103},[93,1502,1503],{"class":125}," WITH_CUBLAS=",[93,1505,44],{"class":103},[93,1507,1508],{"class":103}," -DWITH_IPP=ON",[93,1510,1423],{"class":103},[93,1512,1513],{"class":125}," CUDA_GENERATION=Auto",[93,1515,1423],{"class":103},[93,1517,1518],{"class":125}," WITH_FFMPEG=ON",[93,1520,1521],{"class":125},"  ..\u002F\n",[11,1523,1524],{},"J'active lors de la compilation le maximum d'optimisation dont CUDA. J'active également FFMPEG sans lequel le nombre de fichier reconnu baisse énormément sur ma machine. Après\navoir lancé cmake j'obtiens le résultat suivant :",[85,1526,1529],{"className":1527,"code":1528,"language":391},[389],"-- General configuration for OpenCV 2.4.13.1 =====================================\n--   Version control:               2.4.13.1-48-gac118ae\n--\n--   Platform:\n--     Host:                        Linux 3.16.0-4-amd64 x86_64\n--     CMake:                       3.6.2\n--     CMake generator:             Unix Makefiles\n--     CMake build tool:            \u002Fusr\u002Fbin\u002Fmake\n--     Configuration:               RELEASE\n--\n--   C\u002FC++:\n--     Built as dynamic libs?:      YES\n--     C++ Compiler:                \u002Fusr\u002Fbin\u002Fc++  (ver 4.9.2)\n--     C++ flags (Release):         -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -O3 -DNDEBUG  -DNDEBUG\n--     C++ flags (Debug):           -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -g  -O0 -DDEBUG -D_DEBUG\n--     C Compiler:                  \u002Fusr\u002Fbin\u002Fcc\n--     C flags (Release):           -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wno-narrowing -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -O3 -DNDEBUG  -DNDEBUG\n--     C flags (Debug):             -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wno-narrowing -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -g  -O0 -DDEBUG -D_DEBUG\n--     Linker flags (Release):\n--     Linker flags (Debug):\n--     ccache:                      NO\n--     Precompiled headers:         YES\n--\n--   OpenCV modules:\n--     To be built:                 core flann imgproc highgui features2d calib3d ml video legacy objdetect photo gpu ocl nonfree contrib java python stitching superres ts videostab\n--     Disabled:                    world\n--     Disabled by dependency:      -\n--     Unavailable:                 androidcamera dynamicuda viz\n--\n--   GUI:\n--     QT:                          NO\n--     GTK+ 2.x:                    YES (ver 2.24.25)\n--     GThread :                    YES (ver 2.42.1)\n--     GtkGlExt:                    NO\n--     OpenGL support:              NO\n--     VTK support:                 NO\n--\n--   Media I\u002FO:\n--     ZLib:                        \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibz.so (ver 1.2.8)\n--     JPEG:                        \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibjpeg.so (ver )\n--     PNG:                         \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibpng.so (ver 1.2.50)\n--     TIFF:                        \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibtiff.so (ver 42 - 4.0.3)\n--     JPEG 2000:                   \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibjasper.so (ver 1.900.1)\n--     OpenEXR:                     \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibImath.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibIlmImf.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibIex.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibHalf.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibIlmThread.so (ver 1.6.1)\n--\n--   Video I\u002FO:\n--     DC1394 1.x:                  NO\n--     DC1394 2.x:                  YES (ver 2.2.3)\n--     FFMPEG:                      YES\n--       codec:                     YES (ver 56.1.0)\n--       format:                    YES (ver 56.1.0)\n--       util:                      YES (ver 54.3.0)\n--       swscale:                   YES (ver 3.0.0)\n--       resample:                  YES (ver 2.1.0)\n--       gentoo-style:              YES\n--     GStreamer:                   NO\n--     OpenNI:                      NO\n--     OpenNI PrimeSensor Modules:  NO\n--     PvAPI:                       NO\n--     GigEVisionSDK:               NO\n--     UniCap:                      NO\n--     UniCap ucil:                 NO\n--     V4L\u002FV4L2:                    NO\u002FYES\n--     XIMEA:                       NO\n--     Xine:                        NO\n--\n--   Other third-party libraries:\n--     Use IPP:                     IPP not found\n--     Use Eigen:                   NO\n--     Use TBB:                     NO\n--     Use OpenMP:                  NO\n--     Use GCD                      NO\n--     Use Concurrency              NO\n--     Use C=:                      NO\n--     Use Cuda:                    YES (ver 7.5)\n--     Use OpenCL:                  YES\n--\n--   NVIDIA CUDA\n--     Use CUFFT:                   YES\n--     Use CUBLAS:                  YES\n--     USE NVCUVID:                 NO\n--     NVIDIA GPU arch:             21\n--     NVIDIA PTX archs:\n--     Use fast math:               YES\n--     Tiny gpu module:             NO\n--\n--   OpenCL:\n--     Version:                     dynamic\n--     Include path:                \u002Fhome\u002Fphoenix\u002FDeveloppement\u002FExternalSoftware\u002Fopencv\u002F3rdparty\u002Finclude\u002Fopencl\u002F1.2\n--     Use AMD FFT:                 NO\n--     Use AMD BLAS:                NO\n--\n--   Python:\n--     Interpreter:                 \u002Fusr\u002Fbin\u002Fpython2 (ver 2.7.10)\n--     Libraries:                   \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibpython2.7.so (ver 2.7.10rc1)\n--     numpy:                       \u002Fusr\u002Flib\u002Fpython2.7\u002Fdist-packages\u002Fnumpy\u002Fcore\u002Finclude (ver 1.8.2)\n--     packages path:               lib\u002Fpython2.7\u002Fdist-packages\n--\n--   Java:\n--     ant:                         \u002Fusr\u002Fbin\u002Fant (ver 1.9.4)\n--     JNI:                         \u002Fusr\u002Flib\u002Fjvm\u002Fjava-7-openjdk-amd64\u002Finclude \u002Fusr\u002Flib\u002Fjvm\u002Fjava-7-openjdk-amd64\u002Finclude \u002Fusr\u002Flib\u002Fjvm\u002Fjava-7-openjdk-amd64\u002Finclude\n--     Java tests:                  YES\n--\n--   Documentation:\n--     Build Documentation:         NO\n--     Sphinx:                      NO\n--     PdfLaTeX compiler:           \u002Fusr\u002Fbin\u002Fpdflatex\n--     Doxygen:                     YES (\u002Fusr\u002Fbin\u002Fdoxygen)\n--\n--   Tests and samples:\n--     Tests:                       YES\n--     Performance tests:           YES\n--     C\u002FC++ Examples:              NO\n--\n--   Install path:                  \u002Fhome\u002Fphoenix\u002Fusr\u002Flocal\n--\n--   cvconfig.h is in:              \u002Fhome\u002Fphoenix\u002FDeveloppement\u002FExternalSoftware\u002Fopencv\u002Fbuild\n-- -----------------------------------------------------------------\n",[81,1530,1528],{"__ignoreMap":42},[11,1532,1533,1534,1537,1538,1541,1542,1545],{},"Pour que la compilation se déroule sans problème, il vous faudra installer certains paquets sur votre distribution. Sur Debian Jessie, j'ai installé ",[81,1535,1536],{},"nvidia-cuda-toolkit"," en\nversion ",[81,1539,1540],{},"7.5.18-4~bpo8+1",". Comme vous pouvez les voir c'est une version qui provient du repository de backports. La version ",[81,1543,1544],{},"6.0.37-5"," ne me permettait pas d'activer CUDA. J'ai\ndonc du monter l'ensemble du driver propriétaire sur mon poste de développement.",[11,1547,1548],{},"Rasssurez-vous, si vous ne voulez pas utiliser les backports ou ne pas utiliser de driver propriétaire, vous pouvez tester le programme dans sa version CPU. :)",[11,1550,1551],{},"Voici comment le code a été ré-écrit pour utiliser le GPU à la place du CPU:",[85,1553,1555],{"className":921,"code":1554,"language":923,"meta":42,"style":42},"struct BufferPSNR {                                    \u002F\u002F Optimized GPU versions\n    \u002F\u002F Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later.\n    cv::gpu::GpuMat gF1, gF2, gI1, gI2, gs, t1,t2;\n\n    cv::gpu::GpuMat buf;\n};\n\ndouble getPSNR_GPU_optimized(const cv::Mat& F1, const cv::Mat& F2, BufferPSNR& b) {\n    b.gF1.upload(F1);\n    b.gF2.upload(F2);\n\n    cv::gpu::resize(b.gF1, b.gI1, cv::Size(160, 120));\n    cv::gpu::resize(b.gF2, b.gI2, cv::Size(160, 120));\n\n    b.gI1.convertTo(b.t1, CV_32F);\n    b.gI2.convertTo(b.t2, CV_32F);\n\n    cv::gpu::absdiff(b.t1.reshape(1), b.t2.reshape(1), b.gs);\n    cv::gpu::multiply(b.gs, b.gs, b.gs);\n\n    double sse = cv::gpu::sum(b.gs, b.buf)[0];\n\n    if( sse \u003C= 1e-10) \u002F\u002F for small values return zero\n        return std::numeric_limits\u003Cdouble>::infinity();\n    else {\n        double mse = sse \u002F(double)(F1.channels() * F1.total());\n        double psnr = 10.0*log10((255*255)\u002Fmse);\n        return psnr;\n    }\n}\n",[81,1556,1557,1571,1576,1581,1585,1590,1595,1599,1642,1660,1676,1680,1725,1766,1770,1794,1817,1821,1877,1914,1918,1960,1964,1987,2006,2014,2053,2084,2091,2096],{"__ignoreMap":42},[93,1558,1559,1562,1565,1568],{"class":95,"line":96},[93,1560,1561],{"class":421},"struct",[93,1563,1564],{"class":945}," BufferPSNR",[93,1566,1567],{"class":176}," {",[93,1569,1570],{"class":1049},"                                    \u002F\u002F Optimized GPU versions\n",[93,1572,1573],{"class":95,"line":113},[93,1574,1575],{"class":1049},"    \u002F\u002F Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later.\n",[93,1577,1578],{"class":95,"line":119},[93,1579,1580],{"class":176},"    cv::gpu::GpuMat gF1, gF2, gI1, gI2, gs, t1,t2;\n",[93,1582,1583],{"class":95,"line":246},[93,1584,268],{"emptyLinePlaceholder":267},[93,1586,1587],{"class":95,"line":252},[93,1588,1589],{"class":176},"    cv::gpu::GpuMat buf;\n",[93,1591,1592],{"class":95,"line":258},[93,1593,1594],{"class":176},"};\n",[93,1596,1597],{"class":95,"line":264},[93,1598,268],{"emptyLinePlaceholder":267},[93,1600,1601,1603,1606,1608,1610,1612,1614,1616,1618,1620,1622,1624,1626,1628,1630,1632,1635,1637,1640],{"class":95,"line":271},[93,1602,930],{"class":421},[93,1604,1605],{"class":99}," getPSNR_GPU_optimized",[93,1607,936],{"class":176},[93,1609,939],{"class":421},[93,1611,942],{"class":176},[93,1613,946],{"class":945},[93,1615,949],{"class":421},[93,1617,953],{"class":952},[93,1619,956],{"class":176},[93,1621,939],{"class":421},[93,1623,942],{"class":176},[93,1625,946],{"class":945},[93,1627,949],{"class":421},[93,1629,967],{"class":952},[93,1631,956],{"class":176},[93,1633,1634],{"class":945},"BufferPSNR",[93,1636,949],{"class":421},[93,1638,1639],{"class":952}," b",[93,1641,970],{"class":176},[93,1643,1644,1647,1649,1652,1654,1657],{"class":95,"line":277},[93,1645,1646],{"class":945},"    b",[93,1648,221],{"class":176},[93,1650,1651],{"class":945},"gF1",[93,1653,221],{"class":176},[93,1655,1656],{"class":99},"upload",[93,1658,1659],{"class":176},"(F1);\n",[93,1661,1662,1664,1666,1669,1671,1673],{"class":95,"line":283},[93,1663,1646],{"class":945},[93,1665,221],{"class":176},[93,1667,1668],{"class":945},"gF2",[93,1670,221],{"class":176},[93,1672,1656],{"class":99},[93,1674,1675],{"class":176},"(F2);\n",[93,1677,1678],{"class":95,"line":288},[93,1679,268],{"emptyLinePlaceholder":267},[93,1681,1682,1684,1687,1690,1692,1694,1697,1699,1701,1703,1705,1707,1710,1713,1715,1717,1719,1721,1723],{"class":95,"line":293},[93,1683,984],{"class":176},[93,1685,1686],{"class":945},"gpu",[93,1688,1689],{"class":176},"::",[93,1691,987],{"class":99},[93,1693,936],{"class":176},[93,1695,1696],{"class":945},"b",[93,1698,221],{"class":176},[93,1700,1651],{"class":1130},[93,1702,956],{"class":176},[93,1704,1696],{"class":945},[93,1706,221],{"class":176},[93,1708,1709],{"class":1130},"gI1",[93,1711,1712],{"class":176},", cv::",[93,1714,993],{"class":99},[93,1716,936],{"class":176},[93,1718,998],{"class":103},[93,1720,956],{"class":176},[93,1722,1003],{"class":103},[93,1724,1006],{"class":176},[93,1726,1727,1729,1731,1733,1735,1737,1739,1741,1743,1745,1747,1749,1752,1754,1756,1758,1760,1762,1764],{"class":95,"line":298},[93,1728,984],{"class":176},[93,1730,1686],{"class":945},[93,1732,1689],{"class":176},[93,1734,987],{"class":99},[93,1736,936],{"class":176},[93,1738,1696],{"class":945},[93,1740,221],{"class":176},[93,1742,1668],{"class":1130},[93,1744,956],{"class":176},[93,1746,1696],{"class":945},[93,1748,221],{"class":176},[93,1750,1751],{"class":1130},"gI2",[93,1753,1712],{"class":176},[93,1755,993],{"class":99},[93,1757,936],{"class":176},[93,1759,998],{"class":103},[93,1761,956],{"class":176},[93,1763,1003],{"class":103},[93,1765,1006],{"class":176},[93,1767,1768],{"class":95,"line":1114},[93,1769,268],{"emptyLinePlaceholder":267},[93,1771,1772,1774,1776,1778,1780,1782,1784,1786,1788,1791],{"class":95,"line":1176},[93,1773,1646],{"class":945},[93,1775,221],{"class":176},[93,1777,1709],{"class":945},[93,1779,221],{"class":176},[93,1781,1060],{"class":99},[93,1783,936],{"class":176},[93,1785,1696],{"class":945},[93,1787,221],{"class":176},[93,1789,1790],{"class":1130},"t1",[93,1792,1793],{"class":176},", CV_32F);\n",[93,1795,1796,1798,1800,1802,1804,1806,1808,1810,1812,1815],{"class":95,"line":1181},[93,1797,1646],{"class":945},[93,1799,221],{"class":176},[93,1801,1751],{"class":945},[93,1803,221],{"class":176},[93,1805,1060],{"class":99},[93,1807,936],{"class":176},[93,1809,1696],{"class":945},[93,1811,221],{"class":176},[93,1813,1814],{"class":1130},"t2",[93,1816,1793],{"class":176},[93,1818,1819],{"class":95,"line":1211},[93,1820,268],{"emptyLinePlaceholder":267},[93,1822,1823,1825,1827,1829,1831,1833,1835,1837,1839,1841,1844,1846,1848,1851,1853,1855,1857,1859,1861,1863,1865,1867,1869,1871,1874],{"class":95,"line":1236},[93,1824,984],{"class":176},[93,1826,1686],{"class":945},[93,1828,1689],{"class":176},[93,1830,1043],{"class":99},[93,1832,936],{"class":176},[93,1834,1696],{"class":945},[93,1836,221],{"class":176},[93,1838,1790],{"class":945},[93,1840,221],{"class":176},[93,1842,1843],{"class":99},"reshape",[93,1845,936],{"class":176},[93,1847,44],{"class":103},[93,1849,1850],{"class":176},"), ",[93,1852,1696],{"class":945},[93,1854,221],{"class":176},[93,1856,1814],{"class":945},[93,1858,221],{"class":176},[93,1860,1843],{"class":99},[93,1862,936],{"class":176},[93,1864,44],{"class":103},[93,1866,1850],{"class":176},[93,1868,1696],{"class":945},[93,1870,221],{"class":176},[93,1872,1873],{"class":1130},"gs",[93,1875,1876],{"class":176},");\n",[93,1878,1879,1881,1883,1885,1888,1890,1892,1894,1896,1898,1900,1902,1904,1906,1908,1910,1912],{"class":95,"line":1248},[93,1880,984],{"class":176},[93,1882,1686],{"class":945},[93,1884,1689],{"class":176},[93,1886,1887],{"class":99},"multiply",[93,1889,936],{"class":176},[93,1891,1696],{"class":945},[93,1893,221],{"class":176},[93,1895,1873],{"class":1130},[93,1897,956],{"class":176},[93,1899,1696],{"class":945},[93,1901,221],{"class":176},[93,1903,1873],{"class":1130},[93,1905,956],{"class":176},[93,1907,1696],{"class":945},[93,1909,221],{"class":176},[93,1911,1873],{"class":1130},[93,1913,1876],{"class":176},[93,1915,1916],{"class":95,"line":1297},[93,1917,268],{"emptyLinePlaceholder":267},[93,1919,1920,1922,1924,1926,1928,1930,1932,1935,1937,1939,1941,1943,1945,1947,1949,1952,1955,1957],{"class":95,"line":1335},[93,1921,1117],{"class":421},[93,1923,1120],{"class":176},[93,1925,1074],{"class":421},[93,1927,942],{"class":176},[93,1929,1686],{"class":945},[93,1931,1689],{"class":176},[93,1933,1934],{"class":99},"sum",[93,1936,936],{"class":176},[93,1938,1696],{"class":945},[93,1940,221],{"class":176},[93,1942,1873],{"class":1130},[93,1944,956],{"class":176},[93,1946,1696],{"class":945},[93,1948,221],{"class":176},[93,1950,1951],{"class":1130},"buf",[93,1953,1954],{"class":176},")[",[93,1956,1137],{"class":103},[93,1958,1959],{"class":176},"];\n",[93,1961,1962],{"class":95,"line":1343},[93,1963,268],{"emptyLinePlaceholder":267},[93,1965,1967,1969,1971,1973,1975,1977,1979,1981,1984],{"class":95,"line":1966},23,[93,1968,1184],{"class":421},[93,1970,1187],{"class":176},[93,1972,1190],{"class":421},[93,1974,1193],{"class":103},[93,1976,1196],{"class":1130},[93,1978,1199],{"class":176},[93,1980,1202],{"class":103},[93,1982,1983],{"class":176},")",[93,1985,1986],{"class":1049}," \u002F\u002F for small values return zero\n",[93,1988,1990,1992,1994,1996,1998,2000,2002,2004],{"class":95,"line":1989},24,[93,1991,1214],{"class":421},[93,1993,1217],{"class":176},[93,1995,1220],{"class":945},[93,1997,177],{"class":176},[93,1999,930],{"class":421},[93,2001,1227],{"class":176},[93,2003,1230],{"class":99},[93,2005,1233],{"class":176},[93,2007,2009,2012],{"class":95,"line":2008},25,[93,2010,2011],{"class":421},"    else",[93,2013,1245],{"class":176},[93,2015,2017,2019,2022,2024,2026,2028,2030,2032,2034,2037,2039,2041,2043,2045,2047,2049,2051],{"class":95,"line":2016},26,[93,2018,1251],{"class":421},[93,2020,2021],{"class":176}," mse ",[93,2023,1074],{"class":421},[93,2025,1120],{"class":176},[93,2027,1261],{"class":421},[93,2029,936],{"class":176},[93,2031,930],{"class":421},[93,2033,1269],{"class":176},[93,2035,2036],{"class":945},"F1",[93,2038,221],{"class":176},[93,2040,1277],{"class":99},[93,2042,1280],{"class":176},[93,2044,1283],{"class":421},[93,2046,953],{"class":945},[93,2048,221],{"class":176},[93,2050,1291],{"class":99},[93,2052,1294],{"class":176},[93,2054,2056,2058,2060,2062,2064,2066,2069,2071,2073,2075,2077,2079,2081],{"class":95,"line":2055},27,[93,2057,1251],{"class":421},[93,2059,1302],{"class":176},[93,2061,1074],{"class":421},[93,2063,1307],{"class":103},[93,2065,1283],{"class":421},[93,2067,2068],{"class":99},"log10",[93,2070,1316],{"class":176},[93,2072,1319],{"class":103},[93,2074,1283],{"class":421},[93,2076,1319],{"class":103},[93,2078,1983],{"class":176},[93,2080,1261],{"class":421},[93,2082,2083],{"class":176},"mse);\n",[93,2085,2087,2089],{"class":95,"line":2086},28,[93,2088,1214],{"class":421},[93,2090,1340],{"class":176},[93,2092,2094],{"class":95,"line":2093},29,[93,2095,1346],{"class":176},[93,2097,2099],{"class":95,"line":2098},30,[93,2100,2101],{"class":176},"}\n",[11,2103,2104,2105,2107,2108,2111],{},"L'utilisation de la structure ",[81,2106,1634],{}," permet de ne pas perdre de performance lors de l'initialisation relativement lourde des objets ",[81,2109,2110],{},"GpuMat",". Sans cela, l'utilisation du Gpu\nserait moins performant que la version Cpu.",[57,2113,2115],{"id":2114},"lexpérience","L'expérience",[11,2117,2118],{},"Maintenant place à l'expérience. Nous allons lancer notre programme sur notre jeu d'essai comprenant les vidéos issue des DVD, ainsi que les vidéos recompressées pour l'expérience.\nSi l'expérence se déroule bien l'algorithme devrait nous detecter les fichiers dupliqués, ainsi que les fichiers recompressés.",[663,2120,2122],{"id":2121},"lancement-et-selection-des-dossiers","Lancement et selection des dossiers",[11,2124,2125],{},"La première étape est la sélection des dossiers que l'on souhaite comparer. Le programme ira lire récursivement l'ensemble des dossiers pour y trouver l'ensemble des fichiers\nvidéos.",[11,2127,2128],{},"La sélection d'un projet provient de mon envie de départ de pouvoir enregistrer l'avancement du projet au fur et à mesure. Cette étape n'a pas été réalisée mais l'existance du mode\nprojet existe toujours.",[11,2130,2131],{},[2132,2133],"img",{"alt":2134,"src":2135},"Selection du projet","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-1.png",[11,2137,2138],{},"Une fois le dossier projet choisi, il faut sélectionner la liste des dossiers contenant les vidéos et lancer le programme ...",[663,2140,2142],{"id":2141},"comparaison-des-vidéos","Comparaison des vidéos",[11,2144,2145,2146,221],{},"Dans cette étape le programme compare l'ensemble des vidéos présentes dans les dossiers. L'ensemble du processus tourne dans des threads afin de ne pas figer l'IHM, grâce à l'API\n",[81,2147,2148],{},"QtConcurrent",[11,2150,2151,2155],{},[2132,2152],{"alt":2153,"src":2154},"Recherche","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-2.png",[2132,2156],{"alt":2157,"src":2158},"Recherche encore","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-3.png",[11,2160,2161],{},"Les étapes de la recherche sont donc :",[47,2163,2164,2167,2170,2173,2176],{},[50,2165,2166],{},"Constitution de la liste des fichiers",[50,2168,2169],{},"Récupération des méta-données",[50,2171,2172],{},"Création de la liste des paires de fichiers (en filtrant sur la durée)",[50,2174,2175],{},"Calcul du PSNR pour chaque paire de fichiers",[50,2177,2178],{},"Filtrage pour ne garder que les paires de fichiers dont le PSNR est supérieur à 30 db.",[11,2180,2181,2182,2184,2185,2187,2188,2191,2192,2195],{},"Lors de mon développement je me suis basé sur l'API ",[81,2183,2148],{}," pour faire les différentes étapes. Faisant beaucoup de développement NodeJS ces derniers temps je suis habitué à\nl'utilisation des promesses et de leur enchainement pour faire des processus complexes. J'ai trouvé dommage de ne pas retrouver la même chose dans l'API ",[81,2186,2148],{},". Pour\nreproduire un équivalent, lorsqu'un ",[81,2189,2190],{},"QFuture","se termine, le signal émis par ",[81,2193,2194],{},"QFutureWatcher"," est récupérer par un SLOT du moteur qui s'occupe de lancer l'étape suivante.",[663,2197,2199],{"id":2198},"la-page-de-résultat","La page de résultat",[11,2201,2202],{},"La page de résultat liste les vidéos considérées identiques suite à l'étude d'une des images. Un coup d'oeil visuel permet alors de se faire un avis sur la question, et de\nsupprimer la vidéo que l'on souhaite.",[11,2204,2205],{},[2132,2206],{"alt":2207,"src":2208},"Résultat 1","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-4.png",[11,2210,2211],{},"Comme on peut le voir le programme retrouve les vidéos dont l'image est identique, ainsi que les films qui ont été redimensionnés sans trop de soucis.\nLe problème se situe alors au niveau du bruit qui est généré. Plusieurs films sont considérés comme proches alors que complètement différents. Pour régler ce problème, comparer\nplusieurs images d'une même vidéo à des timestamps différents pourrait peut-être régler le problème.",[11,2213,2214],{},[2132,2215],{"alt":2216,"src":2217},"Résultat 2","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-5.png",[11,2219,2220],{},[2221,2222,2223],"strong",{},"Je vous conseille de vérifier manuellement la qualité et la similarité de chaque vidéo manuellement avant toute suppression.",[57,2225,2227],{"id":2226},"conclusion","Conclusion",[11,2229,2230],{},"En conclusion, j'ai trouvé l'expérience intéressante, et maintenant qu'elle est terminée, je vais pouvoir en tenter une autre ;). Est-ce que le programme continuera d'évoluer ?\nPourquoi pas ? Cela dépendera des PR (Pull Request) et des demandes faites par les utilisateurs, ainsi que du temps que j'ai envie de passer dessus.",[698,2232,2233],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}",{"title":42,"searchDepth":113,"depth":113,"links":2235},[2236,2237,2238,2239,2240,2241,2242,2243],{"id":742,"depth":119,"text":743},{"id":770,"depth":119,"text":771},{"id":865,"depth":119,"text":866},{"id":1349,"depth":119,"text":1350},{"id":2114,"depth":119,"text":2115},{"id":2121,"depth":113,"text":2122},{"id":2141,"depth":113,"text":2142},{"id":2198,"depth":113,"text":2199,"children":2244},[2245],{"id":2226,"depth":119,"text":2227},"Programmation","programmation","2016-12-10",{"type":8,"value":2250},[2251,2253,2255,2257,2259,2261,2265],[57,2252,743],{"id":742},[11,2254,746],{},[11,2256,749],{},[11,2258,752],{},[11,2260,755],{},[11,2262,2263],{},[14,2264,760],{},[11,2266,763,2267,221],{},[37,2268,766],{"href":766,"rel":2269},[198],{"planet":267},"\u002Fpost\u002Ffindsimilarity",{"title":737,"description":42},"findsimilarity","posts\u002FProgrammation\u002F2016-12-10-findsimilarity",[2276,2277,2278,731],"qt","opencv","video","LR-Ko6CoDmyd4rJj7msRVs_pkiaov-yO2_RYmY_C6cE",{"id":2281,"title":2282,"author":6,"body":2283,"category":708,"categorySlug":709,"date":2337,"description":2287,"excerpt":2338,"extension":724,"location":725,"meta":2356,"navigation":267,"path":2357,"published":267,"seo":2358,"slug":2359,"stem":2360,"tags":2361,"timeToRead":113,"__hash__":2363},"posts\u002Fposts\u002FLogiciels\u002F2009-04-11-kde4-devient-instable.md","KDE4 devient instable ...",{"type":8,"value":2284,"toc":2335},[2285,2288,2291,2305,2312,2315,2329,2332],[11,2286,2287],{},"Title: KDE4 devient instable ...\nTags: debian, kde, qt",[11,2289,2290],{},"Titre trolleur, cela ne veux pas dire que KDE 4 est moins stable\nqu'avant mais qu'il a passé une étape supplémentaire chez Debian.",[11,2292,2293,2294,2297,2298,2301,2302,221],{},"Voilà quelques jours déjà que la distribution ",[2221,2295,2296],{},"Gnu\u002FDebian"," intègre\n",[2221,2299,2300],{},"KDE 4"," dans sa version instable. C'est une grande nouvelle car cela\nva permettre à toute les personnes se trouvant en instable et n'ayant\npas touchées à la branche expérimental de Debian de pouvoir tester ",[2221,2303,2304],{},"KDE\n4",[11,2306,2307,2308,2311],{},"Malheureusement pour les ",[14,2309,2310],{},"traumatisé de KDE 4",", l'intégration de ce\ndernier remplace KDE 3.5 et ne viens pas se positionner en parallèle. Il\nme semble pourtant que lors du passage de KDE 2 à KDE 3, il était\npossible d'avoir les deux bureaux en même temps sur sa machine et de\nswitcher de l'un à l'autre sans difficulté. Cela signifie donc peut-être\naussi la perte d'utilisateur pour KDE sous Gnu\u002FDebian au profit d'un\nautre environnement de bureau.",[11,2313,2314],{},"Afin d'effectuer la migration en douceur, Gnu\u002FDebian propose un\nassistant de migration de KDE du nom de Kaboom. Cet assistant, démarrer\nlors de la première connexion de l'utilisateur et vous propose alors de\nsauvegarder votre ancien dossier de configuration KDE 3 et de migrer le\ndossier .kde vers la version 4.",[11,2316,2317,2321,2325],{},[2132,2318],{"alt":2319,"src":2320},"kaboom1","\u002FLogiciels\u002Fkde4-devient-instable\u002Fkaboom1_s.jpg",[2132,2322],{"alt":2323,"src":2324},"kaboom2","\u002FLogiciels\u002Fkde4-devient-instable\u002Fkaboom2_s.jpg",[2132,2326],{"alt":2327,"src":2328},"kaboom3","\u002FLogiciels\u002Fkde4-devient-instable\u002Fkaboom3_s.jpg",[11,2330,2331],{},"Je viens donc de passer à KDE 4 et m'émerveille des nouvelles\nfonctionnalités de KWin (cube, placement des fenêtres) que je n'avais\npas avant, car n'avait jamais installé Compiz ...",[11,2333,2334],{},"Bonne installation,",{"title":42,"searchDepth":113,"depth":113,"links":2336},[],"2009-04-11",{"type":8,"value":2339},[2340,2342,2344,2352],[11,2341,2287],{},[11,2343,2290],{},[11,2345,2293,2346,2297,2348,2301,2350,221],{},[2221,2347,2296],{},[2221,2349,2300],{},[2221,2351,2304],{},[11,2353,2307,2354,2311],{},[14,2355,2310],{},{},"\u002Fpost\u002Fkde4-devient-instable",{"title":2282,"description":2287},"kde4-devient-instable","posts\u002FLogiciels\u002F2009-04-11-kde4-devient-instable",[731,2362,2276],"kde","Oj3jqh3sY77Ym6LYJ2tDQUMs5alVjGgGLz0JxRhI7k8",{"id":2365,"title":2366,"author":6,"body":2367,"category":2380,"categorySlug":2786,"date":2787,"description":42,"excerpt":2788,"extension":724,"location":725,"meta":2804,"navigation":267,"path":2805,"published":267,"seo":2806,"slug":2807,"stem":2808,"tags":2809,"timeToRead":246,"__hash__":2812},"posts\u002Fposts\u002FXINX\u002F2009-03-06-xinx-0.8.0.0.md","XINX 0.8.0.0",{"type":8,"value":2368,"toc":2779},[2369,2375,2381,2389,2392,2400,2403,2406,2410,2421,2428,2431,2436,2442,2459,2463,2469,2472,2475,2479,2484,2492,2678,2688,2692,2695,2703,2705,2709,2721,2724,2728,2731,2737,2740,2770,2776],[11,2370,2371],{},[2132,2372],{"alt":2373,"src":2374},"Editeur","\u002FXINX\u002Fxinx-0.8.0.0\u002Fediteur_de_texte.png",[11,2376,2377,2378,221],{},"J'ai le plaisir de vous annoncer la sortie de la nouvelle version de\n",[2221,2379,2380],{},"XINX",[11,2382,2383,2385,2386,221],{},[2221,2384,2380],{}," est un éditeur de projet web basé sur les technologies XSL,\nHTML, CSS, JavaScript. Il possède un mode projet permettant de dériver\nles projets à la manière des consultants GCE de l'entreprise ",[14,2387,2388],{},"Generix\nGroup",[11,2390,2391],{},"Cette dernière version a mis beaucoup de temps pour sortir et est moins\ncomplète que celle que je souhaitais faire à l'origine. Cela viens de\nplusieurs raison :",[47,2393,2394,2397],{},[50,2395,2396],{},"de gros changement interne",[50,2398,2399],{},"moins de temps le soir et le week-end.",[11,2401,2402],{},"La prochaine version 0.8.1 contiendra les développements que je n'ai pu\nmettre dans cette version. N'hésitez pas à faire un retour sur les\nproblèmes que vous rencontrez pour que ces derniers soit également\ncorriger pour la prochaine version.",[11,2404,2405],{},"Nous allons détailler ci-dessous les différentes amélioration incluse\ndans cette version.",[57,2407,2409],{"id":2408},"qcodeedit-le-nouvel-éditeur","QCodeEdit - Le nouvel éditeur",[11,2411,2412,2413,2416,2417,2420],{},"L'éditeur de texte de cette version a complètement été remplacé. On est\npassé de QTextEdit (",[14,2414,2415],{},"éditeur de texte de Qt Software",") à QCodeEdit\n(",[14,2418,2419],{},"Développé par Luc Bruant aka fullmetalcoder","). Heureusement que les\nAPIs de ces deux éditeurs sont très similaire ce qui a permis\nd'effectuer les changements assez rapidement (quelques mois au lieu de\nquelques années ;) ).",[11,2422,2423,2424,2427],{},"Je tiens également à remercier ",[14,2425,2426],{},"fullmetalcoder"," de sa réactivité sur le\ndéveloppement de QCodeEdit.",[11,2429,2430],{},"Les nouveautés qu'apportent ce nouvel éditeur par rapport à QTextEdit\nsont :",[47,2432,2433],{},[50,2434,2435],{},"le folding",[11,2437,2438],{},[2132,2439],{"alt":2440,"src":2441},"Mark","\u002FXINX\u002Fxinx-0.8.0.0\u002Fmarqueur.png",[47,2443,2444,2447,2453,2456],{},[50,2445,2446],{},"les couleurs de fond sur les marque pages et les erreurs (pour une\nplus grande clarté).",[50,2448,2449,2450,1983],{},"un chargement plus rapide des fichiers (exemple :\n",[81,2451,2452],{},"configuration.xml",[50,2454,2455],{},"pourvoir faire des sélections verticales (à l'aide de Ctrl+Shift)\ncomme le font certain éditeurs",[50,2457,2458],{},"afficher les correspondances de parenthèses",[57,2460,2462],{"id":2461},"test-du-xpath","Test du XPATH",[11,2464,2465],{},[2132,2466],{"alt":2467,"src":2468},"X-Quey","\u002FXINX\u002Fxinx-0.8.0.0\u002Fxpath.png",[11,2470,2471],{},"Cette boite de dialogue permet d'exécuter un X-PATH (ou une requête\nXQuery) sur un fichier de présentation. Cela permet de tester rapidement\nles X-PATH compliqué avant de lancer la page sous le navigateur.",[11,2473,2474],{},"Il est possible de saisir des X-PATH relativement à un X-PATH\nsélectionné dans le flux de présentation ou de manière absolu.",[57,2476,2478],{"id":2477},"script-automatique","Script automatique",[11,2480,2481,2483],{},[2221,2482,2380],{}," permet également d'appeler un script lors de la sauvegarde d'un\nfichier. Pour cela un nouvel objet permettant de modifier le contenue de\nl'éditeur plus facilement a été créé.Comme exemple à la sauvegarde de\nscript ont été mis à jours :",[47,2485,2486,2489],{},[50,2487,2488],{},"La mise à jours des key('url-param','') par des X-PATH",[50,2490,2491],{},"La mise à jours du nom du fichier dans les feuilles de styles.",[85,2493,2497],{"className":2494,"code":2495,"language":2496,"meta":42,"style":42},"language-javascript shiki shiki-themes one-dark-pro","obj.beforeSave = function() {\n  this.run();\n};\n\nobj.run = function() {\n  var search = new DocumentSearch(textEdit);\n\n  search.options.regExp = true;\n  search.searchText = \"key\\\\('url-param', '(.*)'\\\\)\";\n  search.replaceText =\n    \"\u002Flayout_data\u002Fapplication_data\u002Ftemporaire\u002Fparam[@name='\\\\1']\";\n\n  while (search.next()) {}\n};\n","javascript",[81,2498,2499,2517,2529,2533,2537,2551,2574,2578,2601,2628,2640,2652,2656,2674],{"__ignoreMap":42},[93,2500,2501,2504,2506,2509,2511,2514],{"class":95,"line":96},[93,2502,2503],{"class":945},"obj",[93,2505,221],{"class":176},[93,2507,2508],{"class":99},"beforeSave",[93,2510,425],{"class":795},[93,2512,2513],{"class":421}," function",[93,2515,2516],{"class":176},"() {\n",[93,2518,2519,2522,2524,2527],{"class":95,"line":113},[93,2520,2521],{"class":945},"  this",[93,2523,221],{"class":176},[93,2525,2526],{"class":99},"run",[93,2528,1233],{"class":176},[93,2530,2531],{"class":95,"line":119},[93,2532,1594],{"class":176},[93,2534,2535],{"class":95,"line":246},[93,2536,268],{"emptyLinePlaceholder":267},[93,2538,2539,2541,2543,2545,2547,2549],{"class":95,"line":252},[93,2540,2503],{"class":945},[93,2542,221],{"class":176},[93,2544,2526],{"class":99},[93,2546,425],{"class":795},[93,2548,2513],{"class":421},[93,2550,2516],{"class":176},[93,2552,2553,2556,2559,2561,2564,2567,2569,2572],{"class":95,"line":258},[93,2554,2555],{"class":421},"  var",[93,2557,2558],{"class":1130}," search",[93,2560,425],{"class":795},[93,2562,2563],{"class":421}," new",[93,2565,2566],{"class":99}," DocumentSearch",[93,2568,936],{"class":176},[93,2570,2571],{"class":1130},"textEdit",[93,2573,1876],{"class":176},[93,2575,2576],{"class":95,"line":264},[93,2577,268],{"emptyLinePlaceholder":267},[93,2579,2580,2583,2585,2588,2590,2593,2595,2598],{"class":95,"line":271},[93,2581,2582],{"class":945},"  search",[93,2584,221],{"class":176},[93,2586,2587],{"class":945},"options",[93,2589,221],{"class":176},[93,2591,2592],{"class":1130},"regExp",[93,2594,425],{"class":795},[93,2596,2597],{"class":103}," true",[93,2599,2600],{"class":176},";\n",[93,2602,2603,2605,2607,2610,2612,2615,2618,2621,2623,2626],{"class":95,"line":277},[93,2604,2582],{"class":945},[93,2606,221],{"class":176},[93,2608,2609],{"class":1130},"searchText",[93,2611,425],{"class":795},[93,2613,2614],{"class":125}," \"key",[93,2616,2617],{"class":795},"\\\\",[93,2619,2620],{"class":125},"('url-param', '(.*)'",[93,2622,2617],{"class":795},[93,2624,2625],{"class":125},")\"",[93,2627,2600],{"class":176},[93,2629,2630,2632,2634,2637],{"class":95,"line":283},[93,2631,2582],{"class":945},[93,2633,221],{"class":176},[93,2635,2636],{"class":1130},"replaceText",[93,2638,2639],{"class":795}," =\n",[93,2641,2642,2645,2647,2650],{"class":95,"line":288},[93,2643,2644],{"class":125},"    \"\u002Flayout_data\u002Fapplication_data\u002Ftemporaire\u002Fparam[@name='",[93,2646,2617],{"class":795},[93,2648,2649],{"class":125},"1']\"",[93,2651,2600],{"class":176},[93,2653,2654],{"class":95,"line":293},[93,2655,268],{"emptyLinePlaceholder":267},[93,2657,2658,2661,2663,2666,2668,2671],{"class":95,"line":298},[93,2659,2660],{"class":421},"  while",[93,2662,1264],{"class":176},[93,2664,2665],{"class":945},"search",[93,2667,221],{"class":176},[93,2669,2670],{"class":99},"next",[93,2672,2673],{"class":176},"()) {}\n",[93,2675,2676],{"class":95,"line":1114},[93,2677,1594],{"class":176},[11,2679,2680,2681,2683,2684,2687],{},"La nouveauté se trouve dans l'ajout de quatre nouvelle méthodes. La\nméthode utilisé ici est ",[81,2682,2508],{}," et permet de faire des\nmodifications au texte avant sauvegarde. Tout comme ",[81,2685,2686],{},"afterSave"," permet\nd'effectuer des modifications au texte après sauvegarde. Nous avons\négalement deux méthodes beforeLoad et afterLoad pour les opérations à\nfaire lors de la lecture.",[57,2689,2691],{"id":2690},"autres-nouveautés","Autres nouveautés",[11,2693,2694],{},"Bien sur cet version apporte aussi d'autres nouveautés comme :",[47,2696,2697,2700],{},[50,2698,2699],{},"la fermeture automatique de balise",[50,2701,2702],{},"des nouvelles options, ...",[57,2704,60],{"id":59},[73,2706,2708],{"id":2707},"sous-mswindows","Sous Ms\u002FWindows",[11,2710,2711,2712,2714,2715,2720],{},"Pour installer la dernière version de ",[2221,2713,2380],{}," vous pouvez télécharger\nle ",[37,2716,2719],{"href":2717,"rel":2718},"http:\u002F\u002Fxinx.shadoware.org\u002Fdownloads\u002F40",[198],"programme d'installation"," et le lancer. Il remplacera\nautomatiquement l'ancienne version.",[11,2722,2723],{},"Si vous avez des fichiers de données modifiés (template, script, ...)\npensez à les sauvegarder.",[73,2725,2727],{"id":2726},"sous-gnulinux","Sous Gnu\u002FLinux",[11,2729,2730],{},"Sous Debian, vous pouvez ajouter le dépôt dans le fichier\n\u002Fetc\u002Fapt\u002Fsources.list. Ainsi vous serez automatiquement prévenu des\nmises à jours éventuelle.",[85,2732,2735],{"className":2733,"code":2734,"language":391},[389],"deb http:\u002F\u002Fapt.shadoware.org\u002F sid main\n",[81,2736,2734],{"__ignoreMap":42},[11,2738,2739],{},"puis dans une console :",[85,2741,2743],{"className":87,"code":2742,"language":89,"meta":42,"style":42},"sudo apt-cache search xinx\nsudo aptitude install xinx\n",[81,2744,2745,2758],{"__ignoreMap":42},[93,2746,2747,2750,2753,2755],{"class":95,"line":96},[93,2748,2749],{"class":99},"sudo",[93,2751,2752],{"class":125}," apt-cache",[93,2754,2558],{"class":125},[93,2756,2757],{"class":125}," xinx\n",[93,2759,2760,2762,2765,2768],{"class":95,"line":113},[93,2761,2749],{"class":99},[93,2763,2764],{"class":125}," aptitude",[93,2766,2767],{"class":125}," install",[93,2769,2757],{"class":125},[11,2771,2772,2773,2775],{},"Pour les autres distributions ou pour les machines MacOS vous pouvez\ncompiler ",[2221,2774,2380],{}," à partir des sources.",[698,2777,2778],{},"html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":42,"searchDepth":113,"depth":113,"links":2780},[2781,2782,2783,2784,2785],{"id":2408,"depth":119,"text":2409},{"id":2461,"depth":119,"text":2462},{"id":2477,"depth":119,"text":2478},{"id":2690,"depth":119,"text":2691},{"id":59,"depth":119,"text":60},"xinx","2009-03-06",{"type":8,"value":2789},[2790,2794,2798],[11,2791,2792],{},[2132,2793],{"alt":2373,"src":2374},[11,2795,2377,2796,221],{},[2221,2797,2380],{},[11,2799,2800,2385,2802,221],{},[2221,2801,2380],{},[14,2803,2388],{},{},"\u002Fpost\u002Fxinx-0.8.0.0",{"title":2366,"description":42},"xinx-0.8.0.0","posts\u002FXINX\u002F2009-03-06-xinx-0.8.0.0",[731,2786,2810,2276,2811],"generix-group","xml","2LVP73RxAuNHiNwoDLW7n9zx61WC1hKRo8CPZUCY5rk",{"id":2814,"title":2815,"author":6,"body":2816,"category":708,"categorySlug":709,"date":4409,"description":4410,"excerpt":4411,"extension":724,"location":725,"meta":4432,"navigation":267,"path":4433,"published":267,"seo":4434,"slug":4435,"stem":4436,"tags":4437,"timeToRead":288,"__hash__":4438},"posts\u002Fposts\u002FLogiciels\u002F2021-03-02-gridsome-mise-a-jour-du-blog.md","Mise à jour du blog vers gridsome",{"type":8,"value":2817,"toc":4400},[2818,2832,2846,2849,2852,2866,2869,2873,2876,2879,2907,2910,2914,2917,2945,2948,2951,2980,2983,3008,3011,3069,3072,3141,3145,3152,3160,3163,3263,3281,3349,3352,3392,3399,3472,3475,3534,3543,3546,3613,3616,3698,3701,3705,3708,3711,3714,3717,3727,3730,3737,4010,4019,4023,4026,4029,4040,4043,4073,4076,4079,4082,4090,4099,4108,4116,4119,4129,4309,4312,4364,4367,4369,4372,4383,4397],[11,2819,2820,2821,2825,2826,2831],{},"J'avais migré en Janvier 2013 mon ",[37,2822,2824],{"href":2823},"\u002Fpages\u002Fmigration-to-pelican","blog vers Pelican"," en venant de\n",[37,2827,2830],{"href":2828,"rel":2829},"https:\u002F\u002Ffr.dotclear.org\u002F",[198],"Dotclear",". Pelican est un générateur de site statique en Python. J'avais\nalors dû faire l'impasse sur les commentaires mais au bénéfice d'un site performant et avec une\nsurface d'attaque plus faible.",[11,2833,2834,2835,2840,2841,221],{},"Plus tard, en décembre 2016, j'avais changé le thème pour ",[37,2836,2839],{"href":2837,"rel":2838},"https:\u002F\u002Fgithub.com\u002Falexandrevicenzi\u002FFlex",[198],"alexandrevicenzi\u002FFlex",".\nAujoud'hui je vais vous montrer la migration de mon blog vers ",[37,2842,2845],{"href":2843,"rel":2844},"https:\u002F\u002Fgridsome.org\u002F",[198],"Gridsome",[11,2847,2848],{},"Je reste sur un générateur de site statique car, j'aime l'idée d'un site à la fois rapide et\nimmuable tant que je ne décide pas de modifier le contenu mon blog. Alors pourquoi changer de Pelican\nvers Gridsome ?",[11,2850,2851],{},"Gridsome est un générateur de site statique écrit en Javascript. Il a la particularité de pouvoir générer\ndes pages depuis des fichiers au format Markdown mais aussi depuis des CMS Headless, ou de n'importe\nquelles autres API et ce grace à une API commune en GraphQL.",[11,2853,2854,2855,2860,2861,221],{},"En plus de générer le site statique au format HTML, Gridsome fonctionne comme une SPA",[34,2856,2857],{},[37,2858,44],{"href":39,"ariaDescribedBy":2859,"dataFootnoteRef":42,"id":43},[41],". Dans une SPA\nles pages sont générées côté client et sont mises à jour lors d'appel API pour récupérer les données. Ainsi\nseul le contenu modifié est rafraichit. Le framework Gridsome se base sur ",[37,2862,2865],{"href":2863,"rel":2864},"https:\u002F\u002Ffr.vuejs.org\u002F",[198],"Vue.Js",[11,2867,2868],{},"Ce qui m'a plus dans Gridsome, c'est l'utilisation de Vue.JS pour générer les pages statiques, mais aussi\nl'utilisation de GraphQL pour requêter le contenu des pages d'une même et unique façon.",[663,2870,2872],{"id":2871},"alternatives","Alternatives",[11,2874,2875],{},"Lors de la migration de mon blog, je voulais une technologie basée sur Vue.Js, car c'est un framework que\nj'apprécie. Mais le plus important, c'est que j'avais besoin de changement (pour le plaisir quoi ;)).",[11,2877,2878],{},"Les générateurs de site statique en Vue.Js sont :",[47,2880,2881,2888,2895,2902],{},[50,2882,2883],{},[37,2884,2887],{"href":2885,"rel":2886},"https:\u002F\u002Fvuepress.vuejs.org\u002F",[198],"VuePress",[50,2889,2890],{},[37,2891,2894],{"href":2892,"rel":2893},"https:\u002F\u002Ffr.nuxtjs.org\u002F",[198],"Nuxt.js",[50,2896,2897],{},[37,2898,2901],{"href":2899,"rel":2900},"https:\u002F\u002Fsaber.land\u002F",[198],"Saber",[50,2903,2904],{},[37,2905,2845],{"href":2843,"rel":2906},[198],[11,2908,2909],{},"Je vous laisse regarder et vous faire votre avis sur les différents framework. J'ai surtout testé VuePress mais\nj'ai changé pour Gridsome quand j'ai vu la simplicité d'implémentation.",[663,2911,2913],{"id":2912},"début-de-limplémentation","Début de l'implémentation",[11,2915,2916],{},"Je voulais changer de technologie, mais j'aimais bien le thème Pelican que j'utilisais. Je n'avais donc pas\nenvie de le changer. Je me suis imposé également les contraintes suivantes:",[47,2918,2919,2922,2933,2936,2939,2942],{},[50,2920,2921],{},"Je ne voulais pas changer les différents chemins d'accès au site (tant que faire se peut).",[50,2923,2924,2925],{},"Je voulais garder les fonctionnalités suivantes:\n",[47,2926,2927,2930],{},[50,2928,2929],{},"Sitemap",[50,2931,2932],{},"Atom",[50,2934,2935],{},"J'avais des billets écrits en Markdown mais aussi 3 pages que je ne voulais pas réécrire en HTML",[50,2937,2938],{},"Garder le même thème",[50,2940,2941],{},"Avoir une page par catégorie et une page avec toutes les catégories",[50,2943,2944],{},"Avoir une page par tag et une page avec tous les tags",[11,2946,2947],{},"Je commence donc à initialiser un projet de zéro avec mes différents billets de blog, les pages (contact,\nanciens-projets, et contact) et les fichiers statiques (les images, les zips, ...).",[11,2949,2950],{},"J'ai du retravailler les différents billets de blog au format Markdown:",[47,2952,2953,2956,2973],{},[50,2954,2955],{},"J'ai dû déplacer les différents attributs du fichier pour les placer dans un frontmatter au format YAML.",[50,2957,2958,2959,2962,2963,2966,2967,221,2970,2972],{},"Les fichiers ont un nom qui porte de l'information. Ainsi, le dossier dans lequel se trouve le fichier\nconstitue le nom de la catégorie, et le nom du fichier porte la date de publication et le ",[81,2960,2961],{},"slug"," (utilisé\npour le chemin dans l'URL).",[2964,2965],"br",{},"Par exemple, avec le fichier: ",[81,2968,2969],{},"Logiciels\u002F2009-02-15_debian-lenny-est-sortie.md",[2964,2971],{},"Avec le changement de paradigme de Gridsome, j'ai dû intégrer ces différentes informations dans le frontmatter.",[50,2974,2975,2976,2979],{},"J'ai également du retravailler les emplacements de fichiers qui étaient préfixés dans Pelican\nde ",[81,2977,2978],{},"|static|\u002Fpublic\u002F...",". Dans Gridsome, il n'y pas de syntaxe particulière, juste le nom du fichier absolu.",[11,2981,2982],{},"Avant j'avais:",[85,2984,2987],{"className":2985,"code":2986,"language":724,"meta":42,"style":42},"language-md shiki shiki-themes one-dark-pro","Title: Debian Lenny est sortie\nTags: debian, kde\n\n![Logo](|static|\u002Fpublic\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[81,2988,2989,2994,2999,3003],{"__ignoreMap":42},[93,2990,2991],{"class":95,"line":96},[93,2992,2993],{},"Title: Debian Lenny est sortie\n",[93,2995,2996],{"class":95,"line":113},[93,2997,2998],{},"Tags: debian, kde\n",[93,3000,3001],{"class":95,"line":119},[93,3002,268],{"emptyLinePlaceholder":267},[93,3004,3005],{"class":95,"line":246},[93,3006,3007],{},"![Logo](|static|\u002Fpublic\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[11,3009,3010],{},"et j'ai corrigé cela en",[85,3012,3014],{"className":2985,"code":3013,"language":724,"meta":42,"style":42},"---\ntitle: Debian Lenny est sortie\ndate: 2009-02-15\ntags:\n  - debian\n  - kde\npublished: True\ncategory: Logiciels\n---\n\n![Logo](\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[81,3015,3016,3021,3026,3031,3036,3041,3046,3051,3056,3060,3064],{"__ignoreMap":42},[93,3017,3018],{"class":95,"line":96},[93,3019,3020],{},"---\n",[93,3022,3023],{"class":95,"line":113},[93,3024,3025],{},"title: Debian Lenny est sortie\n",[93,3027,3028],{"class":95,"line":119},[93,3029,3030],{},"date: 2009-02-15\n",[93,3032,3033],{"class":95,"line":246},[93,3034,3035],{},"tags:\n",[93,3037,3038],{"class":95,"line":252},[93,3039,3040],{},"  - debian\n",[93,3042,3043],{"class":95,"line":258},[93,3044,3045],{},"  - kde\n",[93,3047,3048],{"class":95,"line":264},[93,3049,3050],{},"published: True\n",[93,3052,3053],{"class":95,"line":271},[93,3054,3055],{},"category: Logiciels\n",[93,3057,3058],{"class":95,"line":277},[93,3059,3020],{},[93,3061,3062],{"class":95,"line":283},[93,3063,268],{"emptyLinePlaceholder":267},[93,3065,3066],{"class":95,"line":288},[93,3067,3068],{},"![Logo](\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[11,3070,3071],{},"Afin de pouvoir reprendre mes fichiers au format markdown sans trop de modification, j'ai également installé les\nplugins remark suivants (ce sur quoi Gridsome se base pour transformer le Markdown en HTML) :",[47,3073,3074,3080,3114,3129,3135],{},[50,3075,3076,3079],{},[81,3077,3078],{},"@gridsome\u002Fremark-prismjs",": pour effectuer une coloration syntaxique.",[50,3081,3082,3085,3086],{},[81,3083,3084],{},"remark-inline-links",": pour pouvoir gérer les liens inclus (qui sont ensuite reportés en bas du fichier)",[85,3087,3089],{"className":2985,"code":3088,"language":724,"meta":42,"style":42},"[foo], [foo][], [bar][foo].\n\n![foo], ![foo][], ![bar][foo].\n\n[foo]: http:\u002F\u002Fexample.com \"Example Domain\"\n",[81,3090,3091,3096,3100,3105,3109],{"__ignoreMap":42},[93,3092,3093],{"class":95,"line":96},[93,3094,3095],{},"[foo], [foo][], [bar][foo].\n",[93,3097,3098],{"class":95,"line":113},[93,3099,268],{"emptyLinePlaceholder":267},[93,3101,3102],{"class":95,"line":119},[93,3103,3104],{},"![foo], ![foo][], ![bar][foo].\n",[93,3106,3107],{"class":95,"line":246},[93,3108,268],{"emptyLinePlaceholder":267},[93,3110,3111],{"class":95,"line":252},[93,3112,3113],{},"[foo]: http:\u002F\u002Fexample.com \"Example Domain\"\n",[50,3115,3116,3119,3120],{},[81,3117,3118],{},"remark-attr",": pour pouvoir ajouter des attributs à une image ou à un objet.",[85,3121,3123],{"className":2985,"code":3122,"language":724,"meta":42,"style":42},"![alt](img){attrs} \u002F ![alt](img){ height=50 }\n",[81,3124,3125],{"__ignoreMap":42},[93,3126,3127],{"class":95,"line":96},[93,3128,3122],{},[50,3130,3131,3134],{},[81,3132,3133],{},"remark-toc",": pour pouvoir ajouter un sommaire à certain billet.",[50,3136,3137,3140],{},[81,3138,3139],{},"remark-footnotes",": pour pouvoir ajouter les footnotes.",[663,3142,3144],{"id":3143},"création-du-thème","Création du thème",[11,3146,3147,3148,3151],{},"Le thème Pelican de mon blog est basé sur ",[37,3149,2839],{"href":2837,"rel":3150},[198],".\nJ'ai donc créé un thème gridsome en reprenant le style du thème précédent mais en convertissant les templates\nen Vue.Js.",[11,3153,3154,3155,221],{},"J'ai donc créé ce thème que j'ai mis à disposition ici : ",[37,3156,3159],{"href":3157,"rel":3158},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fgridsome-flex-markdown-starter",[198],"phoenix741\u002Fgridsome-flex-markdown-starter",[11,3161,3162],{},"Voici la configuration de gridsome que j'ai implémenté pour reconstruire le thème du blog:",[85,3164,3168],{"className":3165,"code":3166,"language":3167,"meta":42,"style":42},"language-js shiki shiki-themes one-dark-pro","  plugins: [\n    {\n      use: \"@gridsome\u002Fsource-filesystem\",\n      options: {\n        baseDir: \"content\u002Fposts\",\n        path: \"**\u002F*.md\",\n        typeName: \"Post\",\n        refs: {\n          tags: {\n            typeName: \"Tag\",\n            create: true,\n          },\n          category: {\n            typeName: \"Category\",\n            create: true,\n          },\n        },\n      },\n    },\n","js",[81,3169,3170,3175,3180,3185,3190,3195,3200,3205,3210,3215,3220,3225,3230,3235,3240,3244,3248,3253,3258],{"__ignoreMap":42},[93,3171,3172],{"class":95,"line":96},[93,3173,3174],{},"  plugins: [\n",[93,3176,3177],{"class":95,"line":113},[93,3178,3179],{},"    {\n",[93,3181,3182],{"class":95,"line":119},[93,3183,3184],{},"      use: \"@gridsome\u002Fsource-filesystem\",\n",[93,3186,3187],{"class":95,"line":246},[93,3188,3189],{},"      options: {\n",[93,3191,3192],{"class":95,"line":252},[93,3193,3194],{},"        baseDir: \"content\u002Fposts\",\n",[93,3196,3197],{"class":95,"line":258},[93,3198,3199],{},"        path: \"**\u002F*.md\",\n",[93,3201,3202],{"class":95,"line":264},[93,3203,3204],{},"        typeName: \"Post\",\n",[93,3206,3207],{"class":95,"line":271},[93,3208,3209],{},"        refs: {\n",[93,3211,3212],{"class":95,"line":277},[93,3213,3214],{},"          tags: {\n",[93,3216,3217],{"class":95,"line":283},[93,3218,3219],{},"            typeName: \"Tag\",\n",[93,3221,3222],{"class":95,"line":288},[93,3223,3224],{},"            create: true,\n",[93,3226,3227],{"class":95,"line":293},[93,3228,3229],{},"          },\n",[93,3231,3232],{"class":95,"line":298},[93,3233,3234],{},"          category: {\n",[93,3236,3237],{"class":95,"line":1114},[93,3238,3239],{},"            typeName: \"Category\",\n",[93,3241,3242],{"class":95,"line":1176},[93,3243,3224],{},[93,3245,3246],{"class":95,"line":1181},[93,3247,3229],{},[93,3249,3250],{"class":95,"line":1211},[93,3251,3252],{},"        },\n",[93,3254,3255],{"class":95,"line":1236},[93,3256,3257],{},"      },\n",[93,3259,3260],{"class":95,"line":1248},[93,3261,3262],{},"    },\n",[11,3264,3265,3266,3269,3270,3273,3274,3277,3278,221],{},"Pour commencer, on utilise le module ",[81,3267,3268],{},"@gridsome\u002Fsource-filesystem"," pour lire le contenu du dossier\n",[81,3271,3272],{},"content\u002Fposts\u002F**\u002F*.md",". On définit au plugin comment on créé les ",[81,3275,3276],{},"Tag"," et les ",[81,3279,3280],{},"Category",[85,3282,3284],{"className":3165,"code":3283,"language":3167,"meta":42,"style":42},"    {\n      use: \"@microflash\u002Fgridsome-plugin-feed\",\n      options: {\n        contentTypes: [\"Post\"],\n        rss: {\n          enabled: true,\n          output: \"\u002Ffeed.xml\",\n        },\n        atom: {\n          enabled: true,\n          output: \"\u002Ffeed.atom\",\n        },\n      },\n    },\n",[81,3285,3286,3290,3295,3299,3304,3309,3314,3319,3323,3328,3332,3337,3341,3345],{"__ignoreMap":42},[93,3287,3288],{"class":95,"line":96},[93,3289,3179],{},[93,3291,3292],{"class":95,"line":113},[93,3293,3294],{},"      use: \"@microflash\u002Fgridsome-plugin-feed\",\n",[93,3296,3297],{"class":95,"line":119},[93,3298,3189],{},[93,3300,3301],{"class":95,"line":246},[93,3302,3303],{},"        contentTypes: [\"Post\"],\n",[93,3305,3306],{"class":95,"line":252},[93,3307,3308],{},"        rss: {\n",[93,3310,3311],{"class":95,"line":258},[93,3312,3313],{},"          enabled: true,\n",[93,3315,3316],{"class":95,"line":264},[93,3317,3318],{},"          output: \"\u002Ffeed.xml\",\n",[93,3320,3321],{"class":95,"line":271},[93,3322,3252],{},[93,3324,3325],{"class":95,"line":277},[93,3326,3327],{},"        atom: {\n",[93,3329,3330],{"class":95,"line":283},[93,3331,3313],{},[93,3333,3334],{"class":95,"line":288},[93,3335,3336],{},"          output: \"\u002Ffeed.atom\",\n",[93,3338,3339],{"class":95,"line":293},[93,3340,3252],{},[93,3342,3343],{"class":95,"line":298},[93,3344,3257],{},[93,3346,3347],{"class":95,"line":1114},[93,3348,3262],{},[11,3350,3351],{},"Ensuite on crée un flux RSS et un flux ATOM avec ces posts, afin que les lecteurs qui utilisent\nencore un lecteur de flux, ou les programmes qui s'abonnent directement à mon site fonctionnent encore.",[85,3353,3355],{"className":3165,"code":3354,"language":3167,"meta":42,"style":42},"    {\n      use: \"@gridsome\u002Fsource-filesystem\",\n      options: {\n        baseDir: \"content\u002Fpages\",\n        path: \"*.md\",\n        typeName: \"BlogPage\",\n      },\n    },\n",[81,3356,3357,3361,3365,3369,3374,3379,3384,3388],{"__ignoreMap":42},[93,3358,3359],{"class":95,"line":96},[93,3360,3179],{},[93,3362,3363],{"class":95,"line":113},[93,3364,3184],{},[93,3366,3367],{"class":95,"line":119},[93,3368,3189],{},[93,3370,3371],{"class":95,"line":246},[93,3372,3373],{},"        baseDir: \"content\u002Fpages\",\n",[93,3375,3376],{"class":95,"line":252},[93,3377,3378],{},"        path: \"*.md\",\n",[93,3380,3381],{"class":95,"line":258},[93,3382,3383],{},"        typeName: \"BlogPage\",\n",[93,3385,3386],{"class":95,"line":264},[93,3387,3257],{},[93,3389,3390],{"class":95,"line":271},[93,3391,3262],{},[11,3393,3394,3395,3398],{},"On recommence avec les pages du site, que l'on type comme ",[81,3396,3397],{},"BlogPage",". Ces pages ne seront\npas accessibles via des catégories ou des pages. Il faudra faire des liens directement vers les\npages depuis un menu.",[85,3400,3402],{"className":3165,"code":3401,"language":3167,"meta":42,"style":42},"    {\n      use: \"@gridsome\u002Fplugin-sitemap\",\n      options: {\n        config: {\n          \"\u002Fpost\u002F*\": {\n            changefreq: \"weekly\",\n            priority: 0.5,\n          },\n          \"\u002Fpage\u002F*\": {\n            changefreq: \"monthly\",\n            priority: 0.7,\n          },\n        },\n      },\n    },\n",[81,3403,3404,3408,3413,3417,3422,3427,3432,3437,3441,3446,3451,3456,3460,3464,3468],{"__ignoreMap":42},[93,3405,3406],{"class":95,"line":96},[93,3407,3179],{},[93,3409,3410],{"class":95,"line":113},[93,3411,3412],{},"      use: \"@gridsome\u002Fplugin-sitemap\",\n",[93,3414,3415],{"class":95,"line":119},[93,3416,3189],{},[93,3418,3419],{"class":95,"line":246},[93,3420,3421],{},"        config: {\n",[93,3423,3424],{"class":95,"line":252},[93,3425,3426],{},"          \"\u002Fpost\u002F*\": {\n",[93,3428,3429],{"class":95,"line":258},[93,3430,3431],{},"            changefreq: \"weekly\",\n",[93,3433,3434],{"class":95,"line":264},[93,3435,3436],{},"            priority: 0.5,\n",[93,3438,3439],{"class":95,"line":271},[93,3440,3229],{},[93,3442,3443],{"class":95,"line":277},[93,3444,3445],{},"          \"\u002Fpage\u002F*\": {\n",[93,3447,3448],{"class":95,"line":283},[93,3449,3450],{},"            changefreq: \"monthly\",\n",[93,3452,3453],{"class":95,"line":288},[93,3454,3455],{},"            priority: 0.7,\n",[93,3457,3458],{"class":95,"line":293},[93,3459,3229],{},[93,3461,3462],{"class":95,"line":298},[93,3463,3252],{},[93,3465,3466],{"class":95,"line":1114},[93,3467,3257],{},[93,3469,3470],{"class":95,"line":1176},[93,3471,3262],{},[11,3473,3474],{},"Après, on définit un sitemap qui pourra être utilisé par les moteurs de recherches.",[85,3476,3478],{"className":3165,"code":3477,"language":3167,"meta":42,"style":42},"  ],\n  templates: {\n    Post: (obj) => {\n      return (\n        \"\u002Fpost\u002F\" + obj.fileInfo.name.replace(\u002F^\\d{4}-\\d{2}-\\d{2}[_\\-]\u002F, \"\")\n      );\n    },\n    BlogPage: \"\u002Fpages\u002F:fileInfo__name\",\n    Tag: \"\u002Ftag\u002F:id\",\n    Category: \"\u002Fcategory\u002F:title\",\n  },\n",[81,3479,3480,3485,3490,3495,3500,3505,3510,3514,3519,3524,3529],{"__ignoreMap":42},[93,3481,3482],{"class":95,"line":96},[93,3483,3484],{},"  ],\n",[93,3486,3487],{"class":95,"line":113},[93,3488,3489],{},"  templates: {\n",[93,3491,3492],{"class":95,"line":119},[93,3493,3494],{},"    Post: (obj) => {\n",[93,3496,3497],{"class":95,"line":246},[93,3498,3499],{},"      return (\n",[93,3501,3502],{"class":95,"line":252},[93,3503,3504],{},"        \"\u002Fpost\u002F\" + obj.fileInfo.name.replace(\u002F^\\d{4}-\\d{2}-\\d{2}[_\\-]\u002F, \"\")\n",[93,3506,3507],{"class":95,"line":258},[93,3508,3509],{},"      );\n",[93,3511,3512],{"class":95,"line":264},[93,3513,3262],{},[93,3515,3516],{"class":95,"line":271},[93,3517,3518],{},"    BlogPage: \"\u002Fpages\u002F:fileInfo__name\",\n",[93,3520,3521],{"class":95,"line":277},[93,3522,3523],{},"    Tag: \"\u002Ftag\u002F:id\",\n",[93,3525,3526],{"class":95,"line":283},[93,3527,3528],{},"    Category: \"\u002Fcategory\u002F:title\",\n",[93,3530,3531],{"class":95,"line":288},[93,3532,3533],{},"  },\n",[11,3535,3536,3537,3539,3540,3542],{},"On définit les différentes chemins d'accès pour nos billets, nos pages, nos ",[81,3538,3276],{},",\net nos ",[81,3541,3280],{},". Comme je ne souhaite pas changer les adresses de mes billets sur mon site,\nj'ai défini les adresses sur les mêmes chemins que mon blog sous Pelican.",[11,3544,3545],{},"Comme les noms des fichiers des billets portent la date et le nom du billet, pour retrouver\nle nom du chemin d'origine, je supprime la date pour la génération du chemin.",[85,3547,3549],{"className":3165,"code":3548,"language":3167,"meta":42,"style":42},"  transformers: {\n    remark: {\n      plugins: [\n        [\"@gridsome\u002Fremark-prismjs\", { showLineNumbers: true }],\n        \"remark-inline-links\",\n        [\"remark-toc\", { heading: \"sommaire\" }],\n        \"remark-attr\",\n      ],\n      config: {\n        footnotes: true,\n      },\n    },\n  },\n",[81,3550,3551,3556,3561,3566,3571,3576,3581,3586,3591,3596,3601,3605,3609],{"__ignoreMap":42},[93,3552,3553],{"class":95,"line":96},[93,3554,3555],{},"  transformers: {\n",[93,3557,3558],{"class":95,"line":113},[93,3559,3560],{},"    remark: {\n",[93,3562,3563],{"class":95,"line":119},[93,3564,3565],{},"      plugins: [\n",[93,3567,3568],{"class":95,"line":246},[93,3569,3570],{},"        [\"@gridsome\u002Fremark-prismjs\", { showLineNumbers: true }],\n",[93,3572,3573],{"class":95,"line":252},[93,3574,3575],{},"        \"remark-inline-links\",\n",[93,3577,3578],{"class":95,"line":258},[93,3579,3580],{},"        [\"remark-toc\", { heading: \"sommaire\" }],\n",[93,3582,3583],{"class":95,"line":264},[93,3584,3585],{},"        \"remark-attr\",\n",[93,3587,3588],{"class":95,"line":271},[93,3589,3590],{},"      ],\n",[93,3592,3593],{"class":95,"line":277},[93,3594,3595],{},"      config: {\n",[93,3597,3598],{"class":95,"line":283},[93,3599,3600],{},"        footnotes: true,\n",[93,3602,3603],{"class":95,"line":288},[93,3604,3257],{},[93,3606,3607],{"class":95,"line":293},[93,3608,3262],{},[93,3610,3611],{"class":95,"line":298},[93,3612,3533],{},[11,3614,3615],{},"Enfin, je définis les différents plugins dont j'ai besoin pour que mes différents billets\nfonctionnent correctement.",[85,3617,3619],{"className":3165,"code":3618,"language":3167,"meta":42,"style":42},"  permalinks: {\n    trailingSlash: \"false\",\n    slugify: {\n      use: \"@sindresorhus\u002Fslugify\",\n      options: {\n        decamelize: false,\n      },\n    },\n  },\n  css: {\n    loaderOptions: {\n      less: {\n        \u002F\u002F options here will be passed to less-loader\n      },\n    },\n  },\n};\n",[81,3620,3621,3626,3631,3636,3641,3645,3650,3654,3658,3662,3667,3672,3677,3682,3686,3690,3694],{"__ignoreMap":42},[93,3622,3623],{"class":95,"line":96},[93,3624,3625],{},"  permalinks: {\n",[93,3627,3628],{"class":95,"line":113},[93,3629,3630],{},"    trailingSlash: \"false\",\n",[93,3632,3633],{"class":95,"line":119},[93,3634,3635],{},"    slugify: {\n",[93,3637,3638],{"class":95,"line":246},[93,3639,3640],{},"      use: \"@sindresorhus\u002Fslugify\",\n",[93,3642,3643],{"class":95,"line":252},[93,3644,3189],{},[93,3646,3647],{"class":95,"line":258},[93,3648,3649],{},"        decamelize: false,\n",[93,3651,3652],{"class":95,"line":264},[93,3653,3257],{},[93,3655,3656],{"class":95,"line":271},[93,3657,3262],{},[93,3659,3660],{"class":95,"line":277},[93,3661,3533],{},[93,3663,3664],{"class":95,"line":283},[93,3665,3666],{},"  css: {\n",[93,3668,3669],{"class":95,"line":288},[93,3670,3671],{},"    loaderOptions: {\n",[93,3673,3674],{"class":95,"line":293},[93,3675,3676],{},"      less: {\n",[93,3678,3679],{"class":95,"line":298},[93,3680,3681],{},"        \u002F\u002F options here will be passed to less-loader\n",[93,3683,3684],{"class":95,"line":1114},[93,3685,3257],{},[93,3687,3688],{"class":95,"line":1176},[93,3689,3262],{},[93,3691,3692],{"class":95,"line":1181},[93,3693,3533],{},[93,3695,3696],{"class":95,"line":1211},[93,3697,1594],{},[11,3699,3700],{},"Je termine par modifier la manière dont Gridsome transforme les adresses, toujours pour\nque les chemins de mon blog ne changent pas.",[663,3702,3704],{"id":3703},"développement-des-pages","Développement des pages",[11,3706,3707],{},"Viens ensuite l'écriture des différentes pages, composants et layout pour définir notre thème.\nLe plus compliqué, c'est que le résumé (excerpt) fourni par Gridsome supprime le rendu HTML et\nles retours chariots.",[11,3709,3710],{},"Je voulais que sur la page principale de mon application, je puisse générer un résumé de chaque\nbillet avec le texte et le rendu original du billet.",[11,3712,3713],{},"Pour cela malheureusement Gridsome ne fournit pas une manière simple de générer un résumé. Je vais\nvous présenter mon implémentation afin d'avoir un résumé au format Markdown.",[11,3715,3716],{},"Ma première version a été de créer à partir de contenu HTML le résumé dans un composant vue dédié.\nLors de la génération des pages statiques, le résultat était comme attendu. Par contre lors du\nchangement des pages (en mode SPA), le billet intégral du blog est chargé, pour être tronqué dynamiquement. Ce\nqui est long et pas performant quand cela arrive sur 10 billets à la suite. J'ai donc modifié le système pour\ngénérer les pages côté serveur.",[11,3718,3719,3720,3723,3724,3726],{},"Pour cela j'ai donc modifié le fichier ",[81,3721,3722],{},"gridsome.server.js",". Le problème, c'est que Gridsome ne permet\npas d'appeler un transformer (remark) directement, et ne permet pas de convertir un text en markdown (autrement\nqu'en passant par le plugin ",[81,3725,3268],{},").",[11,3728,3729],{},"J'ai donc dû implémenter l'appel à remark (et les différents plugins) directement dans la partie\nserveur manuellement.",[11,3731,3732,3733,3736],{},"Une fois le fichier HTML obtenu, j'appelle une librairie dédiée ",[81,3734,3735],{},"truncatise"," afin de réduire le contenu\nà quelques paragraphes (4).",[85,3738,3740],{"className":3165,"code":3739,"language":3167,"meta":42,"style":42},"const truncatise = require(\"truncatise\");\n\nconst remark = require(\"remark\");\nconst html = require(\"remark-html\");\n\nconst remarkPrism = require(\"@gridsome\u002Fremark-prismjs\");\nconst remarkInlineLinks = require(\"remark-inline-links\");\nconst remarkToc = require(\"remark-toc\");\nconst remarkAttr = require(\"remark-attr\");\nconst remarkFN = require(\"remark-footnotes\");\n\nmodule.exports = function(api) {\n  api.loadSource(({ addSchemaResolvers }) => {\n    addSchemaResolvers({\n      Post: {\n        excerpt: {\n          type: \"String\",\n          async resolve(obj) {\n            return new Promise((resolve, reject) => {\n              remark()\n                \u002F\u002F Appel des différents plugins utilisé coté config.\n                .use(remarkPrism)\n                .use(remarkInlineLinks)\n                .use(remarkToc, { heading: \"sommaire\" })\n                .use(remarkAttr)\n                .use(remarkFN)\n                \u002F\u002F Génération HTML\n                .use(html)\n                .process(obj.content, function(err, file) {\n                  if (err) {\n                    return reject(err);\n                  }\n\n                  \u002F\u002F On tronque en ne gardant que les 4 premiers paragraphes\n                  resolve(\n                    truncatise(String(file), {\n                      TruncateLength: 4,\n                      TruncateBy: \"paragraphs\",\n                      Strict: false,\n                      StripHTML: false,\n                      Suffix: \" ...\",\n                    })\n                  );\n                });\n            });\n          },\n        },\n      },\n    });\n  });\n};\n",[81,3741,3742,3747,3751,3756,3761,3765,3770,3775,3780,3785,3790,3794,3799,3804,3809,3814,3819,3824,3829,3834,3839,3844,3849,3854,3859,3864,3869,3874,3879,3884,3889,3895,3901,3906,3912,3918,3924,3930,3936,3942,3948,3954,3960,3966,3972,3978,3983,3988,3993,3999,4005],{"__ignoreMap":42},[93,3743,3744],{"class":95,"line":96},[93,3745,3746],{},"const truncatise = require(\"truncatise\");\n",[93,3748,3749],{"class":95,"line":113},[93,3750,268],{"emptyLinePlaceholder":267},[93,3752,3753],{"class":95,"line":119},[93,3754,3755],{},"const remark = require(\"remark\");\n",[93,3757,3758],{"class":95,"line":246},[93,3759,3760],{},"const html = require(\"remark-html\");\n",[93,3762,3763],{"class":95,"line":252},[93,3764,268],{"emptyLinePlaceholder":267},[93,3766,3767],{"class":95,"line":258},[93,3768,3769],{},"const remarkPrism = require(\"@gridsome\u002Fremark-prismjs\");\n",[93,3771,3772],{"class":95,"line":264},[93,3773,3774],{},"const remarkInlineLinks = require(\"remark-inline-links\");\n",[93,3776,3777],{"class":95,"line":271},[93,3778,3779],{},"const remarkToc = require(\"remark-toc\");\n",[93,3781,3782],{"class":95,"line":277},[93,3783,3784],{},"const remarkAttr = require(\"remark-attr\");\n",[93,3786,3787],{"class":95,"line":283},[93,3788,3789],{},"const remarkFN = require(\"remark-footnotes\");\n",[93,3791,3792],{"class":95,"line":288},[93,3793,268],{"emptyLinePlaceholder":267},[93,3795,3796],{"class":95,"line":293},[93,3797,3798],{},"module.exports = function(api) {\n",[93,3800,3801],{"class":95,"line":298},[93,3802,3803],{},"  api.loadSource(({ addSchemaResolvers }) => {\n",[93,3805,3806],{"class":95,"line":1114},[93,3807,3808],{},"    addSchemaResolvers({\n",[93,3810,3811],{"class":95,"line":1176},[93,3812,3813],{},"      Post: {\n",[93,3815,3816],{"class":95,"line":1181},[93,3817,3818],{},"        excerpt: {\n",[93,3820,3821],{"class":95,"line":1211},[93,3822,3823],{},"          type: \"String\",\n",[93,3825,3826],{"class":95,"line":1236},[93,3827,3828],{},"          async resolve(obj) {\n",[93,3830,3831],{"class":95,"line":1248},[93,3832,3833],{},"            return new Promise((resolve, reject) => {\n",[93,3835,3836],{"class":95,"line":1297},[93,3837,3838],{},"              remark()\n",[93,3840,3841],{"class":95,"line":1335},[93,3842,3843],{},"                \u002F\u002F Appel des différents plugins utilisé coté config.\n",[93,3845,3846],{"class":95,"line":1343},[93,3847,3848],{},"                .use(remarkPrism)\n",[93,3850,3851],{"class":95,"line":1966},[93,3852,3853],{},"                .use(remarkInlineLinks)\n",[93,3855,3856],{"class":95,"line":1989},[93,3857,3858],{},"                .use(remarkToc, { heading: \"sommaire\" })\n",[93,3860,3861],{"class":95,"line":2008},[93,3862,3863],{},"                .use(remarkAttr)\n",[93,3865,3866],{"class":95,"line":2016},[93,3867,3868],{},"                .use(remarkFN)\n",[93,3870,3871],{"class":95,"line":2055},[93,3872,3873],{},"                \u002F\u002F Génération HTML\n",[93,3875,3876],{"class":95,"line":2086},[93,3877,3878],{},"                .use(html)\n",[93,3880,3881],{"class":95,"line":2093},[93,3882,3883],{},"                .process(obj.content, function(err, file) {\n",[93,3885,3886],{"class":95,"line":2098},[93,3887,3888],{},"                  if (err) {\n",[93,3890,3892],{"class":95,"line":3891},31,[93,3893,3894],{},"                    return reject(err);\n",[93,3896,3898],{"class":95,"line":3897},32,[93,3899,3900],{},"                  }\n",[93,3902,3904],{"class":95,"line":3903},33,[93,3905,268],{"emptyLinePlaceholder":267},[93,3907,3909],{"class":95,"line":3908},34,[93,3910,3911],{},"                  \u002F\u002F On tronque en ne gardant que les 4 premiers paragraphes\n",[93,3913,3915],{"class":95,"line":3914},35,[93,3916,3917],{},"                  resolve(\n",[93,3919,3921],{"class":95,"line":3920},36,[93,3922,3923],{},"                    truncatise(String(file), {\n",[93,3925,3927],{"class":95,"line":3926},37,[93,3928,3929],{},"                      TruncateLength: 4,\n",[93,3931,3933],{"class":95,"line":3932},38,[93,3934,3935],{},"                      TruncateBy: \"paragraphs\",\n",[93,3937,3939],{"class":95,"line":3938},39,[93,3940,3941],{},"                      Strict: false,\n",[93,3943,3945],{"class":95,"line":3944},40,[93,3946,3947],{},"                      StripHTML: false,\n",[93,3949,3951],{"class":95,"line":3950},41,[93,3952,3953],{},"                      Suffix: \" ...\",\n",[93,3955,3957],{"class":95,"line":3956},42,[93,3958,3959],{},"                    })\n",[93,3961,3963],{"class":95,"line":3962},43,[93,3964,3965],{},"                  );\n",[93,3967,3969],{"class":95,"line":3968},44,[93,3970,3971],{},"                });\n",[93,3973,3975],{"class":95,"line":3974},45,[93,3976,3977],{},"            });\n",[93,3979,3981],{"class":95,"line":3980},46,[93,3982,3229],{},[93,3984,3986],{"class":95,"line":3985},47,[93,3987,3252],{},[93,3989,3991],{"class":95,"line":3990},48,[93,3992,3257],{},[93,3994,3996],{"class":95,"line":3995},49,[93,3997,3998],{},"    });\n",[93,4000,4002],{"class":95,"line":4001},50,[93,4003,4004],{},"  });\n",[93,4006,4008],{"class":95,"line":4007},51,[93,4009,1594],{},[11,4011,4012,4013,4015,4016,3726],{},"C'est ainsi que j'ai converti mon blog vers le framework ",[81,4014,2845],{},". Je vous passe tous les\ndétails (vu que le thème est disponible sur Github sur ",[37,4017,3159],{"href":3157,"rel":4018},[198],[663,4020,4022],{"id":4021},"ajout-dun-système-de-commentaire","Ajout d'un système de commentaire",[11,4024,4025],{},"Ce qu'il manquait sur mon blog précédent c'était un système de commentaires. J'ai donc ajouté\nce système de commentaires à mon blog.",[11,4027,4028],{},"Je ne souhaitais pas ajouter n'importe quel système de commentaires. J'ai donc regardé\nce que je pouvais ajouter sur un site statique. On va retrouver les systèmes de commentaires\nnon libres, et faciles à implémenter (et pas forcément très compatible RGPD).",[47,4030,4031,4034,4037],{},[50,4032,4033],{},"Disqus",[50,4035,4036],{},"Facebook comments",[50,4038,4039],{},"...",[11,4041,4042],{},"De mon coté je me suis dirigé vers les systèmes de commentaires libres:",[47,4044,4045,4052,4059,4066],{},[50,4046,4047],{},[37,4048,4051],{"href":4049,"rel":4050},"https:\u002F\u002Fvssue.js.org\u002F",[198],"Vssue",[50,4053,4054],{},[37,4055,4058],{"href":4056,"rel":4057},"https:\u002F\u002Fgithub.com\u002Fimsun\u002Fgitment",[198],"Gitment",[50,4060,4061],{},[37,4062,4065],{"href":4063,"rel":4064},"https:\u002F\u002Futteranc.es\u002F",[198],"Utterances",[50,4067,4068],{},[37,4069,4072],{"href":4070,"rel":4071},"https:\u002F\u002Fgithub.com\u002Fumputun\u002Fremark42",[198],"Remark42",[11,4074,4075],{},"Remark42 nécessite l'installation d'un programme sur mon serveur. Je l'ai testé mais j'ai été à quelques\nproblème lors de l'intégration avec Gridsome.",[11,4077,4078],{},"Vssue, Gitment et Utterances sont intéressants car ils permettent d'ajouter un système de commentaires\nse basant sur le système de ticketing de Github. Github n'est en lui même pas OpenSource mais les frameworks\nci-dessus le sont.",[11,4080,4081],{},"Je considère que la plupart des personnes lisant mon blog ont un compte Github. Mais n'hésitez pas à commenter\nsi ce n'est pas le cas ;).",[11,4083,4084,4085,221],{},"Vssue était mon système préféré, beau, simple compatible avec Gitea (que j'utilise pour mes sources), Gitlab,\net avec Github également. Mais dû aux limitations de Github et Gitea, il m'était nécessaire de fournir mon\n",[37,4086,4089],{"href":4087,"rel":4088},"https:\u002F\u002Fvssue.js.org\u002Fguide\u002Fgithub.html",[198],"ClientSecret OAuth2",[11,4091,4092,4093,4098],{},"Je n'ai pas apprécié cette idée mais j'ai tout de même voulu tester. Là je me suis rendu compte qu'il\ny avait des problèmes CORS. Le composant passe par ",[37,4094,4097],{"href":4095,"rel":4096},"https:\u002F\u002Fgithub.com\u002FRob--W\u002Fcors-anywhere",[198],"CORS Anywhere"," mais\nce dernier ne peut être utilisé que pour le développement. Ce qui signifie que je dois installer un proxy équivalent\nsur mon serveur. J'ai donc regardé pour autre chose.",[11,4100,4101,4102,4107],{},"J'ai alors testé Utterances. Le problème est que cette librairie n'est pas compatible avec les\napplications de type SPA. On peut voir d'ailleurs quelques commentaires sur le ticket ",[37,4103,4106],{"href":4104,"rel":4105},"https:\u002F\u002Fgithub.com\u002Futterance\u002Futterances\u002Fissues\u002F231",[198],"Github #231","\nde personnes ayant tenté de faire une version du client pour Vue.Js.",[11,4109,4110,4111,3726],{},"Je me suis donc inspiré d'un composant Vue.JS écrit par quelqu'un d'autre (dans le 1er commentaire) pour inclure\nce système sur mon site.\nCe composant a dû être légèrement adapté pour fonctionner (je vous invite à regarder\n",[37,4112,4115],{"href":4113,"rel":4114},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fgridsome-flex-markdown-starter\u002Fblob\u002Fmain\u002Fsrc\u002Fcomponents\u002FGithubComponent.vue",[198],"GithubComponent.vue",[11,4117,4118],{},"Comme le plugin utilise directement l'objet window, il ne peut fonctionner avec le mode SSR (Server Side Rendering) de\nVue.Js. J'ai dû donc désactiver le mode commentaires pour le côté SSR (la génération). Ce n'est pas déconnant car\nde toutes façon les commentaires sont gérés sur Github.",[11,4120,4121,4122,4125,4126,221],{},"Voici comment on fait avec Gridsome. On commence par encadrer le bloc qui ne doit pas faire partie de la génération\ncôté serveur (et qui est donc ",[81,4123,4124],{},"Client Only","). Ce bloc contient notre composant ",[81,4127,4128],{},"GithubComponent",[85,4130,4134],{"className":4131,"code":4132,"language":4133,"meta":42,"style":42},"language-html shiki shiki-themes one-dark-pro","\u003Carticle>\n  ...\n  \u003CClientOnly>\n    \u003Cdiv class=\"commentswrap\">\n      \u003Cdiv id=\"comments\">\n        \u003CGithubComponent\n          :title=\"$page.post.title\"\n          :repo=\"$page.metadata.utterances.repo\"\n          :pathname=\"this.$route.path\"\n          :url=\"url\"\n          :issueTerm=\"$page.metadata.utterances.issueTerm\"\n          :label=\"$page.metadata.utterances.label\"\n        >\u003C\u002FGithubComponent>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002FClientOnly>\n\u003C\u002Farticle>\n","html",[81,4135,4136,4146,4151,4161,4179,4196,4204,4214,4224,4234,4244,4254,4264,4273,4282,4291,4300],{"__ignoreMap":42},[93,4137,4138,4140,4143],{"class":95,"line":96},[93,4139,177],{"class":176},[93,4141,4142],{"class":1130},"article",[93,4144,4145],{"class":176},">\n",[93,4147,4148],{"class":95,"line":113},[93,4149,4150],{"class":176},"  ...\n",[93,4152,4153,4156,4159],{"class":95,"line":119},[93,4154,4155],{"class":176},"  \u003C",[93,4157,4158],{"class":1130},"ClientOnly",[93,4160,4145],{"class":176},[93,4162,4163,4166,4169,4172,4174,4177],{"class":95,"line":246},[93,4164,4165],{"class":176},"    \u003C",[93,4167,4168],{"class":1130},"div",[93,4170,4171],{"class":103}," class",[93,4173,1074],{"class":176},[93,4175,4176],{"class":125},"\"commentswrap\"",[93,4178,4145],{"class":176},[93,4180,4181,4184,4186,4189,4191,4194],{"class":95,"line":252},[93,4182,4183],{"class":176},"      \u003C",[93,4185,4168],{"class":1130},[93,4187,4188],{"class":103}," id",[93,4190,1074],{"class":176},[93,4192,4193],{"class":125},"\"comments\"",[93,4195,4145],{"class":176},[93,4197,4198,4201],{"class":95,"line":258},[93,4199,4200],{"class":176},"        \u003C",[93,4202,4203],{"class":1130},"GithubComponent\n",[93,4205,4206,4209,4211],{"class":95,"line":264},[93,4207,4208],{"class":103},"          :title",[93,4210,1074],{"class":176},[93,4212,4213],{"class":125},"\"$page.post.title\"\n",[93,4215,4216,4219,4221],{"class":95,"line":271},[93,4217,4218],{"class":103},"          :repo",[93,4220,1074],{"class":176},[93,4222,4223],{"class":125},"\"$page.metadata.utterances.repo\"\n",[93,4225,4226,4229,4231],{"class":95,"line":277},[93,4227,4228],{"class":103},"          :pathname",[93,4230,1074],{"class":176},[93,4232,4233],{"class":125},"\"this.$route.path\"\n",[93,4235,4236,4239,4241],{"class":95,"line":283},[93,4237,4238],{"class":103},"          :url",[93,4240,1074],{"class":176},[93,4242,4243],{"class":125},"\"url\"\n",[93,4245,4246,4249,4251],{"class":95,"line":288},[93,4247,4248],{"class":103},"          :issueTerm",[93,4250,1074],{"class":176},[93,4252,4253],{"class":125},"\"$page.metadata.utterances.issueTerm\"\n",[93,4255,4256,4259,4261],{"class":95,"line":293},[93,4257,4258],{"class":103},"          :label",[93,4260,1074],{"class":176},[93,4262,4263],{"class":125},"\"$page.metadata.utterances.label\"\n",[93,4265,4266,4269,4271],{"class":95,"line":298},[93,4267,4268],{"class":176},"        >\u003C\u002F",[93,4270,4128],{"class":1130},[93,4272,4145],{"class":176},[93,4274,4275,4278,4280],{"class":95,"line":1114},[93,4276,4277],{"class":176},"      \u003C\u002F",[93,4279,4168],{"class":1130},[93,4281,4145],{"class":176},[93,4283,4284,4287,4289],{"class":95,"line":1176},[93,4285,4286],{"class":176},"    \u003C\u002F",[93,4288,4168],{"class":1130},[93,4290,4145],{"class":176},[93,4292,4293,4296,4298],{"class":95,"line":1181},[93,4294,4295],{"class":176},"  \u003C\u002F",[93,4297,4158],{"class":1130},[93,4299,4145],{"class":176},[93,4301,4302,4305,4307],{"class":95,"line":1211},[93,4303,4304],{"class":176},"\u003C\u002F",[93,4306,4142],{"class":1130},[93,4308,4145],{"class":176},[11,4310,4311],{},"Enfin on charge le composant dynamiquement dans la partie Javascript.",[85,4313,4315],{"className":3165,"code":4314,"language":3167,"meta":42,"style":42},"export default {\n  components: {\n    PostHeaderTitle,\n    GithubComponent: () =>\n      import(\"..\u002Fcomponents\u002FGithubComponent\")\n        .then((m) => m.default)\n        .catch(),\n  },\n  ...\n}\n",[81,4316,4317,4322,4327,4332,4337,4342,4347,4352,4356,4360],{"__ignoreMap":42},[93,4318,4319],{"class":95,"line":96},[93,4320,4321],{},"export default {\n",[93,4323,4324],{"class":95,"line":113},[93,4325,4326],{},"  components: {\n",[93,4328,4329],{"class":95,"line":119},[93,4330,4331],{},"    PostHeaderTitle,\n",[93,4333,4334],{"class":95,"line":246},[93,4335,4336],{},"    GithubComponent: () =>\n",[93,4338,4339],{"class":95,"line":252},[93,4340,4341],{},"      import(\"..\u002Fcomponents\u002FGithubComponent\")\n",[93,4343,4344],{"class":95,"line":258},[93,4345,4346],{},"        .then((m) => m.default)\n",[93,4348,4349],{"class":95,"line":264},[93,4350,4351],{},"        .catch(),\n",[93,4353,4354],{"class":95,"line":271},[93,4355,3533],{},[93,4357,4358],{"class":95,"line":277},[93,4359,4150],{},[93,4361,4362],{"class":95,"line":283},[93,4363,2101],{},[11,4365,4366],{},"Ces deux opérations permettent d'exclure cette partie lors de la génération des pages statique, mais de\nl'ajouter dynamiquement après le chargement de la page coté client.",[663,4368,2227],{"id":2226},[11,4370,4371],{},"Voici donc comment j'ai mis en place la version Vue.JS de mon blog. N'hésitez pas à m'envoyer un mail\nou lâcher un commentaire.",[11,4373,4374,4375,4378,4379,221],{},"Vous pouvez retrouver le kit de démarrage que j'ai fait pour mon blog sur github: ",[37,4376,3159],{"href":3157,"rel":4377},[198],". Vous pouvez également consulter l'intégration d'utterances sur un site\nen vue ici: ",[37,4380,4382],{"href":4113,"rel":4381},[198],"phoenix741\u002Fgridsome-flex-markdown-starter:GithubComponent.vue",[658,4384,4386,4389],{"className":4385,"dataFootnotes":42},[661],[663,4387,667],{"className":4388,"id":41},[666],[669,4390,4391],{},[50,4392,4393,4394],{"id":673},"Single Page Application ",[37,4395,684],{"href":680,"ariaLabel":681,"className":4396,"dataFootnoteBackref":42},[683],[698,4398,4399],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}",{"title":42,"searchDepth":113,"depth":113,"links":4401},[4402,4403,4404,4405,4406,4407,4408],{"id":2871,"depth":113,"text":2872},{"id":2912,"depth":113,"text":2913},{"id":3143,"depth":113,"text":3144},{"id":3703,"depth":113,"text":3704},{"id":4021,"depth":113,"text":4022},{"id":2226,"depth":113,"text":2227},{"id":41,"depth":113,"text":667},"2021-03-02","J'avais migré en Janvier 2013 mon blog vers Pelican en venant de\nDotclear. Pelican est un générateur de site statique en Python. J'avais\nalors dû faire l'impasse sur les commentaires mais au bénéfice d'un site performant et avec une\nsurface d'attaque plus faible.",{"type":8,"value":4412},[4413,4420,4428,4430],[11,4414,2820,4415,2825,4417,2831],{},[37,4416,2824],{"href":2823},[37,4418,2830],{"href":2828,"rel":4419},[198],[11,4421,2834,4422,2840,4425,221],{},[37,4423,2839],{"href":2837,"rel":4424},[198],[37,4426,2845],{"href":2843,"rel":4427},[198],[11,4429,2848],{},[11,4431,2851],{},{"planet":267},"\u002Fpost\u002Fgridsome-mise-a-jour-du-blog",{"title":2815,"description":4410},"gridsome-mise-a-jour-du-blog","posts\u002FLogiciels\u002F2021-03-02-gridsome-mise-a-jour-du-blog",[732,2496],"92imksbvFvyNB9g-BgsuOp-f1qDd_PCUcic_SG3Gf2o",{"id":4440,"title":4441,"author":6,"body":4442,"category":708,"categorySlug":709,"date":5951,"description":42,"excerpt":5952,"extension":724,"location":725,"meta":5970,"navigation":267,"path":5971,"published":267,"seo":5972,"slug":5973,"stem":5974,"tags":5975,"timeToRead":298,"__hash__":5979},"posts\u002Fposts\u002FLogiciels\u002F2014-03-19-monlivretdemesse-fr.md","Mon livret de messe - Site de génération de livrets au format PDF",{"type":8,"value":4443,"toc":5931},[4444,4448,4450,4459,4467,4470,4509,4524,4528,4531,4534,4537,4544,4547,4550,4553,4556,4559,4562,4568,4571,4582,4585,4594,4598,4604,4608,4611,4621,4624,4627,4630,4633,4641,4644,4886,4889,5343,5346,5352,5356,5359,5362,5366,5369,5372,5376,5383,5387,5396,5400,5403,5407,5419,5422,5426,5429,5432,5436,5443,5446,5558,5561,5564,5572,5575,5579,5585,5588,5591,5602,5606,5609,5616,5619,5633,5637,5644,5652,5660,5663,5667,5673,5676,5679,5758,5761,5764,5767,5845,5848,5919,5922,5925,5928],[57,4445,4447],{"id":4446},"mon-livret-de-messe-générateur-de-pdf","Mon livret de messe - Générateur de PDF",[11,4449,746],{},[11,4451,4452,4453,4458],{},"Ce petit billet pour vous parler d'un site que j'ai développé pour ma femme et dont l'adresse est\n",[37,4454,4457],{"href":4455,"rel":4456},"http:\u002F\u002Fmonlivretdemesse.fr\u002F",[198],"http:\u002F\u002Fmonlivretdemesse.fr",". Ce projet que je développe depuis\nplusieurs années, a été mis à jours récément. Je profite de cette mise à jours pour vous parler de\nce projet fonctionnellement mais aussi techniquement.",[11,4460,4461,4466],{},[37,4462,4465],{"href":4463,"rel":4464},"http:\u002F\u002Fmonlivretdemessse.fr",[198],"Monlivretdemesse.FR"," est un site permettant aux utilisateurs allant\nse marier de générer leur livret de messe au format PDF afin de l'imprimer directement chez eux.\nLes pages du livret ainsi générées sont alors ordonnées de telle manière qu'il suffit de faire une\nimpression recto\u002Fverso puis de plier les feuilles pour avoir son livret. Je me suis chargé\ndu développement de ce site, pendant que ma femme se charge du contenu (donc le contenu des textes,\ndes images, mais aussi et surtout le thème de chaque produit, leur format).",[11,4468,4469],{},"La nouvelle version sortie le 11 mars 2014 permet également la création d'autres types de produits\nafin de générer des",[47,4471,4472,4479,4492,4499,4506],{},[50,4473,4474],{},[37,4475,4478],{"href":4476,"rel":4477},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Fbapteme",[198],"livrets de messe de baptêmes",[50,4480,4481,4486,4487],{},[37,4482,4485],{"href":4483,"rel":4484},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Ffaire-part-mariage",[198],"faire-parts de mariage",",\n",[37,4488,4491],{"href":4489,"rel":4490},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Ffaire-part-bapteme",[198],"de baptêmes",[50,4493,4494],{},[37,4495,4498],{"href":4496,"rel":4497},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Fmenus-mariage",[198],"menus pour la soirée",[50,4500,4501],{},[37,4502,4505],{"href":4503,"rel":4504},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Fmarque-places",[198],"des marques places",[50,4507,4508],{},"et bien d'autres...",[11,4510,4511,4512,4517,4518,4523],{},"Le site a été écrit à l'aide du framework Symfony2 et utilise la bibliothèque\n",[37,4513,4516],{"href":4514,"rel":4515},"http:\u002F\u002Fwww.tcpdf.org\u002F",[198],"TCPDF"," afin de générer les fichiers PDF. Les données sont stockées dans une\nbase de données ",[37,4519,4522],{"href":4520,"rel":4521},"http:\u002F\u002Fwww.mongodb.org\u002F",[198],"MongoDB",". Parmi les données on peut compter les données\nproduits, utilisateurs, mais aussi le cache des PDF générés enregistrés en tant que fichier dans\nGridFS (et ceci afin qu'un livret qui n'a pas été modifié ne soit pas re-généré).",[57,4525,4527],{"id":4526},"symfony2","Symfony2",[11,4529,4530],{},"Symfony 2 est un framework PHP permettant le développement de sites Internet. Il est livré par\ndéfaut avec un ORM: Doctrine qui permet de faire correspondre à une structure de base de données des\nclasses PHP qui seront automatiquement hydratées.",[11,4532,4533],{},"Le développement PHP s'en retrouve presque agréable (je préfère les langages compilés en règle\ngénérale). Le framework est de la même trempe que le Framework Python Django.",[11,4535,4536],{},"Ce dernier m'a d'ailleurs tenté (bien qu'interpreté aussi), mais une lecture rapide de la\ndocumentation m'a donné l'impression d'être un peu moins pratique à utiliser que Symfony2. Peut-être\ncar je ne fais pas de Python.",[11,4538,4539,4540,4543],{},"Pour mon prochain projet j'étudierai l'utilisation de symfony2 vs ruby on rails vs django vs\nnode.js. Mais je ne suis pas sûr qu'au final l'utilisation d'une des technos précédentes m'apporte\nbeaucoup plus par rapport à ce que sait déjà faire un framework comme symfony2. L'avantage d'un tel\nframework réside aussi dans le nombre de ",[14,4541,4542],{},"bundle"," et de librairie utilisables à l'extérieur.",[11,4545,4546],{},"Par rapport à la distribution de base de symfony2 j'ai remplacé la version Doctrine ORM par Doctrine\nODM. Ce qui me permet de me connecter à une base de données MongoDB.",[57,4548,4522],{"id":4549},"mongodb",[11,4551,4552],{},"Mon projet d'abord basé sur une base MySQL a été basculé sur une base de données NoSQL nommée\nMongoDB.",[11,4554,4555],{},"La raison n'est pas technique (je n'ai pas besoin de replication, de sharding, ...., pas assez de\nvisiteurs). J'avais juste envie de tester cette base de données sur mon projet. De plus l'aspect\norienté document est agréable au développement.",[11,4557,4558],{},"En effet, au lieu de stocker les informations dans différentes tables et de tenter d'y accéder à\nl'aide de jointure ou de requête multiple, dans on MongoDB on stocke un document dans un format\nbinaire du JSON (le BSON).",[11,4560,4561],{},"Un panier stocké en base pourra avoir la forme :",[85,4563,4566],{"className":4564,"code":4565,"language":391},[389],"{\n  \"_id\": ObjectId(\"......................\"),\n  \"expiration_updated_at\": ISODate(\"2014-03-17T21:00:34.0Z\"),\n  \"lines\": [\n    {\n      \"product\": {\n        \"product\": \"marque-place-baroque-dore\",\n        \"product_name\": \"Marque-place baroque doré\",\n        \"variant\": \"dore-gris\",\n        \"variant_name\": \"Doré\\\u002FGris\",\n        \"amount\": 7.9\n      },\n      \"custom\": ObjectId(\"....................\")\n    }\n  ],\n  \"lines_count\": NumberInt(1)\n}\n",[81,4567,4565],{"__ignoreMap":42},[11,4569,4570],{},"On retrouve dans un seul document les informations liées au panier et les informations concernant\nchaque ligne. La dénormalisation n'étant pas un problème, on rappelera alors ici le nom du produit,\net le nombre de lignes que l'on pourrait retrouver autrement mais qui permettra un affichage plus\nrapide de cette manière.",[11,4572,4573,4574,4577,4578,4581],{},"Par exemple, sur la page d'acceuil où on affiche le nombre de produit dans le panier, il nous\nsuffira de requêter ",[14,4575,4576],{},"lines_count"," sans toucher à l'attribute ",[14,4579,4580],{},"lines",". Ensuite lors de l'affichage\ndu panier, on récupérera les lignes et les informations du panier en une seule requête et sans\njointure.",[11,4583,4584],{},"Ainsi pour des entités avec forte relation, qu'on ne récupère jamais les unes sans les autres, une\nseule requête permet de récupérer toutes les informations. Par exemple, dans une base relationnelle,\non aura l'habitude de stocker l'entête du panier dans une table, et les lignes dans une autre table.\nAvec Mongo, si je veux récupérer le panier en une requête, je récupère aussi les lignes. Cela\nimplique par contre de ne pouvoir requêter facilement sur les lignes du panier indépendamment de\nleur entête.",[11,4586,4587,4588,4593],{},"Le seul problème que j'ai actuellement avec l'ODM Doctrine est que la mise à jour du panier se fait\nen plusieurs requêtes alors qu'il serait préférable de le faire en une seule pour des questions\nd'atomicité. Ce point fait d'ailleurs l'objet du ticket\n",[37,4589,4592],{"href":4590,"rel":4591},"https:\u002F\u002Fgithub.com\u002Fdoctrine\u002Fmongodb-odm\u002Fissues\u002F437",[198],"437"," du Github du projet.",[57,4595,4597],{"id":4596},"les-différents-modules-du-site","Les différents modules du site",[11,4599,4600,4601,4603],{},"Dans cette section je vais vous parler rapidement des différents ",[14,4602,4542],{}," que j'ai créé pour le site.",[663,4605,4607],{"id":4606},"le-cms","Le CMS",[11,4609,4610],{},"Afin de faciliter l'édition des pages de contenu (Mention légales, Documentation, FAQ) sans toucher\nau code. J'ai écrit un mini CMS. Le but est de stocker dans la base de données les différentes pages\ndu projet, ainsi que les images associées aux pages.",[11,4612,4613,4614,4616,4617,4620],{},"Pour cela j'ai un ",[14,4615,4542],{}," nommé CMS qui utilise la notion d'édition inline de CKEditor. J'ai écris\nmon propre ",[2221,4618,4619],{},"Explorer de media"," qui permet de récupérer et ajouter dans cette même base des\nfichiers à attacher aux différentes pages.",[11,4622,4623],{},"L'adresse de la page est alors décomposée pour récupérer la clé de la page. Une page dont l'adresse\nsera \u002Fpage\u002Fprout aura comme clé en base \u002Fprout. Pour un utilisateur non administrateur (ou anonyme),\nsi la page est trouvée, elle est affichée telle quelle et si elle n'est pas trouvée, une page 404\nest affichée.",[11,4625,4626],{},"En mode administrateur si la page existe, elle est ouverte en mode édition inline pour CKEditor. Si\nla page n'existe pas, elle est ouverte en mode création et en mode inline avec CKEditor. Il est\nainsi super facile pour un administrateur de créer de nouvelles pages.",[11,4628,4629],{},"La partie media permet d'ajouter dans un GridFS dedié les images, et fichiers attachés.",[11,4631,4632],{},"Le module est du coup assez simple, deux documents",[47,4634,4635,4638],{},[50,4636,4637],{},"Page",[50,4639,4640],{},"Media",[11,4642,4643],{},"Le document Page ressemble à ceci:",[85,4645,4649],{"className":4646,"code":4647,"language":4648,"meta":42,"style":42},"language-php shiki shiki-themes one-dark-pro","\u002F**\n  * Page\n  *\n  * @MongoDB\\Document(collection=\"cms_page\")\n  *\u002F\nclass Page {\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\Id(strategy=\"CUSTOM\", options={\"class\"=\"\\Shadoware\\CMSBundle\\Generator\\PageSlugGenerator\"})\n      *\u002F\n    private $slug;\n\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $name;\n\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $title;\n\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $content;\n\n    \u002F**\n      * @var string $lang\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $lang;\n}\n","php",[81,4650,4651,4656,4661,4666,4671,4676,4686,4691,4704,4709,4714,4719,4729,4733,4737,4745,4749,4754,4758,4767,4771,4775,4783,4787,4791,4795,4804,4808,4812,4820,4824,4828,4832,4841,4845,4849,4861,4865,4869,4873,4882],{"__ignoreMap":42},[93,4652,4653],{"class":95,"line":96},[93,4654,4655],{"class":1049},"\u002F**\n",[93,4657,4658],{"class":95,"line":113},[93,4659,4660],{"class":1049},"  * Page\n",[93,4662,4663],{"class":95,"line":119},[93,4664,4665],{"class":1049},"  *\n",[93,4667,4668],{"class":95,"line":246},[93,4669,4670],{"class":1049},"  * @MongoDB\\Document(collection=\"cms_page\")\n",[93,4672,4673],{"class":95,"line":252},[93,4674,4675],{"class":1049},"  *\u002F\n",[93,4677,4678,4681,4684],{"class":95,"line":258},[93,4679,4680],{"class":421},"class",[93,4682,4683],{"class":945}," Page",[93,4685,1245],{"class":176},[93,4687,4688],{"class":95,"line":264},[93,4689,4690],{"class":1049},"    \u002F**\n",[93,4692,4693,4696,4700],{"class":95,"line":271},[93,4694,4695],{"class":1049},"      * ",[93,4697,4699],{"class":4698},"shdRp","@var",[93,4701,4703],{"class":4702},"sKU4T"," string\n",[93,4705,4706],{"class":95,"line":277},[93,4707,4708],{"class":1049},"      *\n",[93,4710,4711],{"class":95,"line":283},[93,4712,4713],{"class":1049},"      * @MongoDB\\Id(strategy=\"CUSTOM\", options={\"class\"=\"\\Shadoware\\CMSBundle\\Generator\\PageSlugGenerator\"})\n",[93,4715,4716],{"class":95,"line":288},[93,4717,4718],{"class":1049},"      *\u002F\n",[93,4720,4721,4724,4727],{"class":95,"line":293},[93,4722,4723],{"class":421},"    private",[93,4725,4726],{"class":1130}," $slug",[93,4728,2600],{"class":176},[93,4730,4731],{"class":95,"line":298},[93,4732,268],{"emptyLinePlaceholder":267},[93,4734,4735],{"class":95,"line":1114},[93,4736,4690],{"class":1049},[93,4738,4739,4741,4743],{"class":95,"line":1176},[93,4740,4695],{"class":1049},[93,4742,4699],{"class":4698},[93,4744,4703],{"class":4702},[93,4746,4747],{"class":95,"line":1181},[93,4748,4708],{"class":1049},[93,4750,4751],{"class":95,"line":1211},[93,4752,4753],{"class":1049},"      * @MongoDB\\String\n",[93,4755,4756],{"class":95,"line":1236},[93,4757,4718],{"class":1049},[93,4759,4760,4762,4765],{"class":95,"line":1248},[93,4761,4723],{"class":421},[93,4763,4764],{"class":1130}," $name",[93,4766,2600],{"class":176},[93,4768,4769],{"class":95,"line":1297},[93,4770,268],{"emptyLinePlaceholder":267},[93,4772,4773],{"class":95,"line":1335},[93,4774,4690],{"class":1049},[93,4776,4777,4779,4781],{"class":95,"line":1343},[93,4778,4695],{"class":1049},[93,4780,4699],{"class":4698},[93,4782,4703],{"class":4702},[93,4784,4785],{"class":95,"line":1966},[93,4786,4708],{"class":1049},[93,4788,4789],{"class":95,"line":1989},[93,4790,4753],{"class":1049},[93,4792,4793],{"class":95,"line":2008},[93,4794,4718],{"class":1049},[93,4796,4797,4799,4802],{"class":95,"line":2016},[93,4798,4723],{"class":421},[93,4800,4801],{"class":1130}," $title",[93,4803,2600],{"class":176},[93,4805,4806],{"class":95,"line":2055},[93,4807,268],{"emptyLinePlaceholder":267},[93,4809,4810],{"class":95,"line":2086},[93,4811,4690],{"class":1049},[93,4813,4814,4816,4818],{"class":95,"line":2093},[93,4815,4695],{"class":1049},[93,4817,4699],{"class":4698},[93,4819,4703],{"class":4702},[93,4821,4822],{"class":95,"line":2098},[93,4823,4708],{"class":1049},[93,4825,4826],{"class":95,"line":3891},[93,4827,4753],{"class":1049},[93,4829,4830],{"class":95,"line":3897},[93,4831,4718],{"class":1049},[93,4833,4834,4836,4839],{"class":95,"line":3903},[93,4835,4723],{"class":421},[93,4837,4838],{"class":1130}," $content",[93,4840,2600],{"class":176},[93,4842,4843],{"class":95,"line":3908},[93,4844,268],{"emptyLinePlaceholder":267},[93,4846,4847],{"class":95,"line":3914},[93,4848,4690],{"class":1049},[93,4850,4851,4853,4855,4858],{"class":95,"line":3920},[93,4852,4695],{"class":1049},[93,4854,4699],{"class":4698},[93,4856,4857],{"class":4702}," string",[93,4859,4860],{"class":1049}," $lang\n",[93,4862,4863],{"class":95,"line":3926},[93,4864,4708],{"class":1049},[93,4866,4867],{"class":95,"line":3932},[93,4868,4753],{"class":1049},[93,4870,4871],{"class":95,"line":3938},[93,4872,4718],{"class":1049},[93,4874,4875,4877,4880],{"class":95,"line":3944},[93,4876,4723],{"class":421},[93,4878,4879],{"class":1130}," $lang",[93,4881,2600],{"class":176},[93,4883,4884],{"class":95,"line":3950},[93,4885,2101],{"class":176},[11,4887,4888],{},"Le controlleur est assez simple :",[85,4890,4892],{"className":4646,"code":4891,"language":4648,"meta":42,"style":42},"\u002F**\n  * @Route(\"\u002Fpage\u002F{slug}\", requirements={\"slug\" = \".+\"})\n  * @Template()\n  *\u002F\npublic function indexAction($slug) {\n    $dm = $this->get('doctrine_mongodb')->getManager();\n\n    $page = $dm->getRepository(\"CMSBundle:Page\")->find($slug);\n    if ($page == null) {\n        if ($this->get('security.context')->isGranted('ROLE_ADMIN')) {\n            $page = new Page();\n            $page->setTitle(\"Titre de la page\");\n            $page->setSlug($slug);\n            $page->setName(\"Nom\");\n            $page->setContent(\"Contenue de la page\");\n        } else {\n            throw $this->createNotFoundException(\"Page not found\");\n        }\n    }\n\n    $request = $this->getRequest();\n    if ($request->getMethod() == \"POST\" && $page != null) {\n        $page->setName($request->get('name'));\n        $page->setTitle($request->get('title'));\n        $page->setContent($request->get('content'));\n\n        $dm->persist($page);\n        $dm->flush();\n    }\n\n    return array('page' => $page);\n}\n",[81,4893,4894,4898,4903,4908,4912,4929,4958,4962,4993,5010,5042,5055,5071,5086,5102,5118,5127,5146,5151,5155,5159,5175,5210,5234,5257,5280,5284,5300,5311,5315,5319,5339],{"__ignoreMap":42},[93,4895,4896],{"class":95,"line":96},[93,4897,4655],{"class":1049},[93,4899,4900],{"class":95,"line":113},[93,4901,4902],{"class":1049},"  * @Route(\"\u002Fpage\u002F{slug}\", requirements={\"slug\" = \".+\"})\n",[93,4904,4905],{"class":95,"line":119},[93,4906,4907],{"class":1049},"  * @Template()\n",[93,4909,4910],{"class":95,"line":246},[93,4911,4675],{"class":1049},[93,4913,4914,4917,4919,4922,4924,4927],{"class":95,"line":252},[93,4915,4916],{"class":421},"public",[93,4918,2513],{"class":421},[93,4920,4921],{"class":99}," indexAction",[93,4923,936],{"class":176},[93,4925,4926],{"class":1130},"$slug",[93,4928,970],{"class":176},[93,4930,4931,4934,4936,4939,4942,4945,4947,4950,4953,4956],{"class":95,"line":258},[93,4932,4933],{"class":1130},"    $dm",[93,4935,425],{"class":795},[93,4937,4938],{"class":945}," $this",[93,4940,4941],{"class":176},"->",[93,4943,4944],{"class":99},"get",[93,4946,936],{"class":176},[93,4948,4949],{"class":125},"'doctrine_mongodb'",[93,4951,4952],{"class":176},")->",[93,4954,4955],{"class":99},"getManager",[93,4957,1233],{"class":176},[93,4959,4960],{"class":95,"line":264},[93,4961,268],{"emptyLinePlaceholder":267},[93,4963,4964,4967,4969,4972,4974,4977,4979,4982,4984,4987,4989,4991],{"class":95,"line":271},[93,4965,4966],{"class":1130},"    $page",[93,4968,425],{"class":795},[93,4970,4971],{"class":1130}," $dm",[93,4973,4941],{"class":176},[93,4975,4976],{"class":99},"getRepository",[93,4978,936],{"class":176},[93,4980,4981],{"class":125},"\"CMSBundle:Page\"",[93,4983,4952],{"class":176},[93,4985,4986],{"class":99},"find",[93,4988,936],{"class":176},[93,4990,4926],{"class":1130},[93,4992,1876],{"class":176},[93,4994,4995,4997,4999,5002,5005,5008],{"class":95,"line":277},[93,4996,1184],{"class":421},[93,4998,1264],{"class":176},[93,5000,5001],{"class":1130},"$page",[93,5003,5004],{"class":795}," ==",[93,5006,5007],{"class":103}," null",[93,5009,970],{"class":176},[93,5011,5012,5015,5017,5020,5022,5024,5026,5029,5031,5034,5036,5039],{"class":95,"line":283},[93,5013,5014],{"class":421},"        if",[93,5016,1264],{"class":176},[93,5018,5019],{"class":945},"$this",[93,5021,4941],{"class":176},[93,5023,4944],{"class":99},[93,5025,936],{"class":176},[93,5027,5028],{"class":125},"'security.context'",[93,5030,4952],{"class":176},[93,5032,5033],{"class":99},"isGranted",[93,5035,936],{"class":176},[93,5037,5038],{"class":125},"'ROLE_ADMIN'",[93,5040,5041],{"class":176},")) {\n",[93,5043,5044,5047,5049,5051,5053],{"class":95,"line":288},[93,5045,5046],{"class":1130},"            $page",[93,5048,425],{"class":795},[93,5050,2563],{"class":421},[93,5052,4683],{"class":945},[93,5054,1233],{"class":176},[93,5056,5057,5059,5061,5064,5066,5069],{"class":95,"line":293},[93,5058,5046],{"class":1130},[93,5060,4941],{"class":176},[93,5062,5063],{"class":99},"setTitle",[93,5065,936],{"class":176},[93,5067,5068],{"class":125},"\"Titre de la page\"",[93,5070,1876],{"class":176},[93,5072,5073,5075,5077,5080,5082,5084],{"class":95,"line":298},[93,5074,5046],{"class":1130},[93,5076,4941],{"class":176},[93,5078,5079],{"class":99},"setSlug",[93,5081,936],{"class":176},[93,5083,4926],{"class":1130},[93,5085,1876],{"class":176},[93,5087,5088,5090,5092,5095,5097,5100],{"class":95,"line":1114},[93,5089,5046],{"class":1130},[93,5091,4941],{"class":176},[93,5093,5094],{"class":99},"setName",[93,5096,936],{"class":176},[93,5098,5099],{"class":125},"\"Nom\"",[93,5101,1876],{"class":176},[93,5103,5104,5106,5108,5111,5113,5116],{"class":95,"line":1176},[93,5105,5046],{"class":1130},[93,5107,4941],{"class":176},[93,5109,5110],{"class":99},"setContent",[93,5112,936],{"class":176},[93,5114,5115],{"class":125},"\"Contenue de la page\"",[93,5117,1876],{"class":176},[93,5119,5120,5123,5125],{"class":95,"line":1181},[93,5121,5122],{"class":176},"        } ",[93,5124,1242],{"class":421},[93,5126,1245],{"class":176},[93,5128,5129,5132,5134,5136,5139,5141,5144],{"class":95,"line":1211},[93,5130,5131],{"class":421},"            throw",[93,5133,4938],{"class":945},[93,5135,4941],{"class":176},[93,5137,5138],{"class":99},"createNotFoundException",[93,5140,936],{"class":176},[93,5142,5143],{"class":125},"\"Page not found\"",[93,5145,1876],{"class":176},[93,5147,5148],{"class":95,"line":1236},[93,5149,5150],{"class":176},"        }\n",[93,5152,5153],{"class":95,"line":1248},[93,5154,1346],{"class":176},[93,5156,5157],{"class":95,"line":1297},[93,5158,268],{"emptyLinePlaceholder":267},[93,5160,5161,5164,5166,5168,5170,5173],{"class":95,"line":1335},[93,5162,5163],{"class":1130},"    $request",[93,5165,425],{"class":795},[93,5167,4938],{"class":945},[93,5169,4941],{"class":176},[93,5171,5172],{"class":99},"getRequest",[93,5174,1233],{"class":176},[93,5176,5177,5179,5181,5184,5186,5189,5191,5194,5197,5200,5203,5206,5208],{"class":95,"line":1343},[93,5178,1184],{"class":421},[93,5180,1264],{"class":176},[93,5182,5183],{"class":1130},"$request",[93,5185,4941],{"class":176},[93,5187,5188],{"class":99},"getMethod",[93,5190,1280],{"class":176},[93,5192,5193],{"class":795},"==",[93,5195,5196],{"class":125}," \"POST\"",[93,5198,5199],{"class":795}," &&",[93,5201,5202],{"class":1130}," $page",[93,5204,5205],{"class":795}," !=",[93,5207,5007],{"class":103},[93,5209,970],{"class":176},[93,5211,5212,5215,5217,5219,5221,5223,5225,5227,5229,5232],{"class":95,"line":1966},[93,5213,5214],{"class":1130},"        $page",[93,5216,4941],{"class":176},[93,5218,5094],{"class":99},[93,5220,936],{"class":176},[93,5222,5183],{"class":1130},[93,5224,4941],{"class":176},[93,5226,4944],{"class":99},[93,5228,936],{"class":176},[93,5230,5231],{"class":125},"'name'",[93,5233,1006],{"class":176},[93,5235,5236,5238,5240,5242,5244,5246,5248,5250,5252,5255],{"class":95,"line":1989},[93,5237,5214],{"class":1130},[93,5239,4941],{"class":176},[93,5241,5063],{"class":99},[93,5243,936],{"class":176},[93,5245,5183],{"class":1130},[93,5247,4941],{"class":176},[93,5249,4944],{"class":99},[93,5251,936],{"class":176},[93,5253,5254],{"class":125},"'title'",[93,5256,1006],{"class":176},[93,5258,5259,5261,5263,5265,5267,5269,5271,5273,5275,5278],{"class":95,"line":2008},[93,5260,5214],{"class":1130},[93,5262,4941],{"class":176},[93,5264,5110],{"class":99},[93,5266,936],{"class":176},[93,5268,5183],{"class":1130},[93,5270,4941],{"class":176},[93,5272,4944],{"class":99},[93,5274,936],{"class":176},[93,5276,5277],{"class":125},"'content'",[93,5279,1006],{"class":176},[93,5281,5282],{"class":95,"line":2016},[93,5283,268],{"emptyLinePlaceholder":267},[93,5285,5286,5289,5291,5294,5296,5298],{"class":95,"line":2055},[93,5287,5288],{"class":1130},"        $dm",[93,5290,4941],{"class":176},[93,5292,5293],{"class":99},"persist",[93,5295,936],{"class":176},[93,5297,5001],{"class":1130},[93,5299,1876],{"class":176},[93,5301,5302,5304,5306,5309],{"class":95,"line":2086},[93,5303,5288],{"class":1130},[93,5305,4941],{"class":176},[93,5307,5308],{"class":99},"flush",[93,5310,1233],{"class":176},[93,5312,5313],{"class":95,"line":2093},[93,5314,1346],{"class":176},[93,5316,5317],{"class":95,"line":2098},[93,5318,268],{"emptyLinePlaceholder":267},[93,5320,5321,5324,5327,5329,5332,5335,5337],{"class":95,"line":3891},[93,5322,5323],{"class":421},"    return",[93,5325,5326],{"class":795}," array",[93,5328,936],{"class":176},[93,5330,5331],{"class":125},"'page'",[93,5333,5334],{"class":176}," => ",[93,5336,5001],{"class":1130},[93,5338,1876],{"class":176},[93,5340,5341],{"class":95,"line":3897},[93,5342,2101],{"class":176},[11,5344,5345],{},"Et enfin la vue Twig contient les références à CKEditor et l'activation du contenu modifiable inliné\npour les projets. Pour l'instant le module est fortement lié à l'application existante. J'ai dans\nl'espoir d'avoir assez de temps un jour pour externaliser ce module afin de pouvoir le partager à\nplus de monde.",[11,5347,5348,5349,5351],{},"Si vous avez envie d'avoir plus d'information sur ce ",[14,5350,4542],{}," n'hesitez pas à me contacter et je\nvous réponderez avec joie.",[663,5353,5355],{"id":5354},"les-produits","Les produits",[11,5357,5358],{},"La partie produits et beaucoup plus liée à l'activité du site. Les produits contiennent pour chaque\ncolori, un modèle dans lequel sont définis la position des différentes informations qui seront par\nla suite saisies par l'utilisateur. Ma femme peut ainsi, après avoir préparé son modèle le\npersonnaliser sur le site.",[11,5360,5361],{},"Un document de type produit personnalisé permet ensuite de spécialiser le produit en y ajoutant les\ninformations saisies par l'utilisateur. C'est ce produit final qui est ensuite acheté par\nl'utilisateur.",[663,5363,5365],{"id":5364},"les-paiements","Les paiements",[11,5367,5368],{},"La partie paiement contient la gestion du panier, de la facturation et le lien avec le site Paypal.\nEn effet on utilise l'API de paypal pour effectuer les paiements car nous n'avions pas l'envie de\ngérer pour ce site un système de paiement onéreux et complexe. Paypal prélève un pourcentage de la\ntransaction, et permet le paiement par carte bancaire des utilisateurs anonymes, ce qui nous\nconvient.",[11,5370,5371],{},"Au niveau du panier, certaines informations sont enregistrées, comme le nombre de lignes dans le\npanier, ainsi que le nom du produit et le nom du coloris choisis afin de faciliter le requêtage et\nl'affichage.",[663,5373,5375],{"id":5374},"la-gestion-des-utilisateurs","La gestion des utilisateurs",[11,5377,5378,5379,5382],{},"La gestion des utilisateurs passent par ",[14,5380,5381],{},"FOS\u002FUserBundle",". Quelques personnalisations ont été\najoutées à ce module afin de correspondre au thème du site et aussi pour ajouter quelques\ninformations.",[663,5384,5386],{"id":5385},"le-site","Le site",[11,5388,5389,5390,137,5393,5395],{},"Ajoute les ",[14,5391,5392],{},"CSS",[14,5394,2496],{}," personnalisés du site. Le theme a été acheté à une époque sur un\nsite proposant des templates, mais à depuis été customizé et adapté pour notre utilisation.",[663,5397,5399],{"id":5398},"ladmin","L'admin",[11,5401,5402],{},"L'interface d'administration permet à femme et à moi d'accéder aux différents produit, d'en créer de\nnouveau, de les modifiers. L'interface se base sur un thème bootstrape et reste assez simple. Seul\nl'utilisateur avec le role d'administrateur peut accéder à cette page.",[663,5404,5406],{"id":5405},"dans-lavenir","Dans l'avenir",[11,5408,5409,5410,5413,5414,5418],{},"Le découpage actuel ne me plaît pas forcément. En effet, je charge des ",[14,5411,5412],{},"bundles"," propres à l'admin\nsur le site alors que ces derniers pourraient être chargés uniquement dans le cadre de l'admin et\ninversement. Je pense qu'appliquer une architecture comme celle décrite ici:\n",[37,5415,5416],{"href":5416,"rel":5417},"http:\u002F\u002Fjolicode.com\u002Fblog\u002Fmultiple-applications-with-symfony2",[198],",\npourrait être une bonne idée pour mieux découper l'application.",[11,5420,5421],{},"Dans le même style, la partie CMS actuelle est liée au site, et j'aimerais la découpler du site pour\npouvoir l'utiliser assez facilement dans d'autres projets.",[57,5423,5425],{"id":5424},"mes-contributions","Mes contributions",[11,5427,5428],{},"Lors du développement de mon projet, j'ai eu besoin de certaines fonctionnalités que je n'ai pas\ntrouvées dans les bundles existants ou qui ne me convenaient pas. Je vous présente ici différents\nprojets que j'ai développé pour pallier à ces manques, sachant que pour l'instant ceux-ci ne sont\npas parfaits et voir même, la documentation peut laisser à désirer (quand aux tests unitaires ils\nsont dans le néant).",[11,5430,5431],{},"Si vous souhaiter aider ou contribuer, n'hésitez pas.",[663,5433,5435],{"id":5434},"collectionbundle","CollectionBundle",[11,5437,5438,5439],{},"Lien: ",[37,5440,5435],{"href":5441,"rel":5442},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002FCollectionBundle",[198],[11,5444,5445],{},"Dans symfony, il est possible d'ajouter dans un formulaire un type collection pour permettre à un\nutilisateur de saisir une collection de sous-éléments (jointure de type OneToMany):",[85,5447,5449],{"className":4646,"code":5448,"language":4648,"meta":42,"style":42},"$builder->add('emails', 'collection', array(\n    \u002F\u002F chaque item du tableau sera un champ « email »\n    'type'   => 'email',\n    \u002F\u002F ces options sont passées à chaque type « email »\n    'options'  => array(\n        'required'  => false,\n        'attr'      => array('class' => 'email-box')\n    ),\n));\n",[81,5450,5451,5479,5484,5497,5502,5514,5526,5549,5554],{"__ignoreMap":42},[93,5452,5453,5456,5458,5461,5463,5466,5468,5471,5473,5476],{"class":95,"line":96},[93,5454,5455],{"class":1130},"$builder",[93,5457,4941],{"class":176},[93,5459,5460],{"class":99},"add",[93,5462,936],{"class":176},[93,5464,5465],{"class":125},"'emails'",[93,5467,956],{"class":176},[93,5469,5470],{"class":125},"'collection'",[93,5472,956],{"class":176},[93,5474,5475],{"class":795},"array",[93,5477,5478],{"class":176},"(\n",[93,5480,5481],{"class":95,"line":113},[93,5482,5483],{"class":1049},"    \u002F\u002F chaque item du tableau sera un champ « email »\n",[93,5485,5486,5489,5492,5495],{"class":95,"line":119},[93,5487,5488],{"class":125},"    'type'",[93,5490,5491],{"class":176},"   => ",[93,5493,5494],{"class":125},"'email'",[93,5496,4486],{"class":176},[93,5498,5499],{"class":95,"line":246},[93,5500,5501],{"class":1049},"    \u002F\u002F ces options sont passées à chaque type « email »\n",[93,5503,5504,5507,5510,5512],{"class":95,"line":252},[93,5505,5506],{"class":125},"    'options'",[93,5508,5509],{"class":176},"  => ",[93,5511,5475],{"class":795},[93,5513,5478],{"class":176},[93,5515,5516,5519,5521,5524],{"class":95,"line":258},[93,5517,5518],{"class":125},"        'required'",[93,5520,5509],{"class":176},[93,5522,5523],{"class":103},"false",[93,5525,4486],{"class":176},[93,5527,5528,5531,5534,5536,5538,5541,5543,5546],{"class":95,"line":264},[93,5529,5530],{"class":125},"        'attr'",[93,5532,5533],{"class":176},"      => ",[93,5535,5475],{"class":795},[93,5537,936],{"class":176},[93,5539,5540],{"class":125},"'class'",[93,5542,5334],{"class":176},[93,5544,5545],{"class":125},"'email-box'",[93,5547,5548],{"class":176},")\n",[93,5550,5551],{"class":95,"line":271},[93,5552,5553],{"class":176},"    ),\n",[93,5555,5556],{"class":95,"line":277},[93,5557,1006],{"class":176},[11,5559,5560],{},"Le problème c'est que dans les formulaires symfony2 il n'est pas possible de gérer des formulaires\ndifférents suivant le sous-type de l'objet (gestion de l'héritage dans l'ORM).",[11,5562,5563],{},"CollectionBundle propose deux nouveaux types :",[47,5565,5566,5569],{},[50,5567,5568],{},"Un type permettant de gérer pour chaque classe fille, un formulaire différent.",[50,5570,5571],{},"Un type permettant de gérer des collections de taille fixe : Exemple toujours 5 éléments, quel\nque soit le nombre d'éléments rééls en base.",[11,5573,5574],{},"Ce bundle est actuellement utilisé uniquement dans la partie admin du site.",[663,5576,5578],{"id":5577},"doctrinemigrationodmbundle","DoctrineMigrationODMBundle",[11,5580,5438,5581],{},[37,5582,5578],{"href":5583,"rel":5584},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002Fdoctrine-migrations-bundle",[198],[11,5586,5587],{},"Pour l'ORM Doctrine, il existe DoctrineMigrationBundle qui permet de faire des migrations de schéma,\nmais il n'existait pas d'équivalent pour l'ODM gérant MongoDB.",[11,5589,5590],{},"Même si MongoDB est schemaless, et que les données peuvent être migrées à l'execution, je ressens le\nbesoin d'avoir la possiblité d'exécuter des scripts lors des changements de version, pour :",[47,5592,5593,5596,5599],{},[50,5594,5595],{},"ajouter de nouvelles données (nécessaires) dans des tables (car on requête sur ces données).",[50,5597,5598],{},"renommage de collection, suite à gros refactoring.",[50,5600,5601],{},"voir autre",[663,5603,5605],{"id":5604},"fpdi-et-fpdf_tpl","FPDI et FPDF_TPL",[11,5607,5608],{},"Pour la génération des PDF, je génère une première version où chaque page est un élément différent,\npuis je me sers de FPDI pour associer les différentes pages sur une même page (avec la mention\nSPECIMEN ou pas).",[11,5610,5611,5612,5615],{},"Afin d'avoir accès à FPDI je me suis créé les dépôts suivants qui fonctionnent avec ceux de\n",[14,5613,5614],{},"tecnick.com\u002Ftcpdf"," pour les utilisateurs de TCPDF.",[11,5617,5618],{},"Liens:",[47,5620,5621,5627],{},[50,5622,5623],{},[37,5624,5625],{"href":5625,"rel":5626},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002Ffpdi",[198],[50,5628,5629],{},[37,5630,5631],{"href":5631,"rel":5632},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002Ffpdf_tpl",[198],[663,5634,5636],{"id":5635},"imageresizerbundle","ImageResizerBundle",[11,5638,5639,5640],{},"Lien : ",[37,5641,5636],{"href":5642,"rel":5643},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002FImageResizerBundle",[198],[11,5645,5646,5647,5651],{},"Dérivé de\n",[37,5648,5649],{"href":5649,"rel":5650},"https:\u002F\u002Fgithub.com\u002Fnresni\u002FImageResizerBundle",[198],", ce\nbundle ajoute",[47,5653,5654,5657],{},[50,5655,5656],{},"des caches supplémentaires",[50,5658,5659],{},"fournit une URL sur la valeur du cache directement (et de générer le cache lors de l'appel de la\ncommande twig). Cette dernière permet de cacher un peu l'URL utilisée pour accéder à l'image\nd'origine. On ne peut accéder alors qu'à l'URL finale. Comme les ids des images sont des ObjectId,\nil n'est pas possible de retrouver par le nom l'URL de l'image d'origine.",[11,5661,5662],{},"Cette extension est utilisée par toute l'application pour l'affichage de toutes les miniatures.",[663,5664,5666],{"id":5665},"piwikbundle","PiwikBundle",[11,5668,5639,5669],{},[37,5670,5666],{"href":5671,"rel":5672},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002FPiwikBundle",[198],[11,5674,5675],{},"Ce plugin a été créé afin de pouvoir ajouter la gestion de Piwik dans Symfony2. Les commandes PIWIK\npeuvent être passées au travers d'un service ou au travers de commandes TWIG. Ce plugin gère\négalement la notion d'e-commerce de PIWIK.",[11,5677,5678],{},"Une fois le plugin installé (via composer), il faut l'activer à l'aide de la configuration suivante:",[85,5680,5684],{"className":5681,"code":5682,"language":5683,"meta":42,"style":42},"language-yaml shiki shiki-themes one-dark-pro","shadoware_piwik:\n  base_url: http:\u002F\u002Fmonpiwik.monsite # URL de base du serveur PIWIK\n  id_site: 1 # N° du site dans PIWIK\n  hidePiwik: false # Indique s'il faut cacher le tracker par un controlleur interne.\n  tokenId: abcedfghijkmn123456789 # Le token id de l'utilisateur (pour le cas où on cache piwik)\n  heartbeat: ~ # Permet de définir quelques attributs activant la fonctionnalité de heatbeat de piwik.\n","yaml",[81,5685,5686,5694,5708,5720,5732,5745],{"__ignoreMap":42},[93,5687,5688,5691],{"class":95,"line":96},[93,5689,5690],{"class":1130},"shadoware_piwik",[93,5692,5693],{"class":176},":\n",[93,5695,5696,5699,5702,5705],{"class":95,"line":113},[93,5697,5698],{"class":1130},"  base_url",[93,5700,5701],{"class":176},": ",[93,5703,5704],{"class":125},"http:\u002F\u002Fmonpiwik.monsite",[93,5706,5707],{"class":1049}," # URL de base du serveur PIWIK\n",[93,5709,5710,5713,5715,5717],{"class":95,"line":119},[93,5711,5712],{"class":1130},"  id_site",[93,5714,5701],{"class":176},[93,5716,44],{"class":103},[93,5718,5719],{"class":1049}," # N° du site dans PIWIK\n",[93,5721,5722,5725,5727,5729],{"class":95,"line":246},[93,5723,5724],{"class":1130},"  hidePiwik",[93,5726,5701],{"class":176},[93,5728,5523],{"class":103},[93,5730,5731],{"class":1049}," # Indique s'il faut cacher le tracker par un controlleur interne.\n",[93,5733,5734,5737,5739,5742],{"class":95,"line":252},[93,5735,5736],{"class":1130},"  tokenId",[93,5738,5701],{"class":176},[93,5740,5741],{"class":125},"abcedfghijkmn123456789",[93,5743,5744],{"class":1049}," # Le token id de l'utilisateur (pour le cas où on cache piwik)\n",[93,5746,5747,5750,5752,5755],{"class":95,"line":258},[93,5748,5749],{"class":1130},"  heartbeat",[93,5751,5701],{"class":176},[93,5753,5754],{"class":103},"~",[93,5756,5757],{"class":1049}," # Permet de définir quelques attributs activant la fonctionnalité de heatbeat de piwik.\n",[11,5759,5760],{},"Une fois la configuration faite, le plugin ajoutera juste avant chaque balise  l'appel à\npiwik (en utilisant la méthode asynchrone). Si la page ne contient pas de balise  ou si elle\nconstitue une page de redirection, les informations seront déportées à l'affichage suivant.",[11,5762,5763],{},"Cette dernière fonctionnalité permet par exemple d'ajouter des éléménts au panier e-commerce piwik\nlors des pages de redirection, et de traiter son affichage dès que possible. Cela a par contre comme\nlimitation de ne pas gérer les conflits.",[11,5765,5766],{},"L'utilisation depuis un controlleur se fait grâce à l'utilisation du service:",[85,5768,5770],{"className":4646,"code":5769,"language":4648,"meta":42,"style":42},"$this->container->get('shadoware_piwik.service')->addEcommerceItem($productId, $productName, $category, $amount);\n$this->container->get('shadoware_piwik.service')->trackEcommerceCartUpdate($totalAmount);\n",[81,5771,5772,5817],{"__ignoreMap":42},[93,5773,5774,5776,5778,5781,5783,5785,5787,5790,5792,5795,5797,5800,5802,5805,5807,5810,5812,5815],{"class":95,"line":96},[93,5775,5019],{"class":945},[93,5777,4941],{"class":176},[93,5779,5780],{"class":1130},"container",[93,5782,4941],{"class":176},[93,5784,4944],{"class":99},[93,5786,936],{"class":176},[93,5788,5789],{"class":125},"'shadoware_piwik.service'",[93,5791,4952],{"class":176},[93,5793,5794],{"class":99},"addEcommerceItem",[93,5796,936],{"class":176},[93,5798,5799],{"class":1130},"$productId",[93,5801,956],{"class":176},[93,5803,5804],{"class":1130},"$productName",[93,5806,956],{"class":176},[93,5808,5809],{"class":1130},"$category",[93,5811,956],{"class":176},[93,5813,5814],{"class":1130},"$amount",[93,5816,1876],{"class":176},[93,5818,5819,5821,5823,5825,5827,5829,5831,5833,5835,5838,5840,5843],{"class":95,"line":113},[93,5820,5019],{"class":945},[93,5822,4941],{"class":176},[93,5824,5780],{"class":1130},[93,5826,4941],{"class":176},[93,5828,4944],{"class":99},[93,5830,936],{"class":176},[93,5832,5789],{"class":125},[93,5834,4952],{"class":176},[93,5836,5837],{"class":99},"trackEcommerceCartUpdate",[93,5839,936],{"class":176},[93,5841,5842],{"class":1130},"$totalAmount",[93,5844,1876],{"class":176},[11,5846,5847],{},"L'utilisation depuis une page twig se fait à l'aide des commandes twig. Par exemple dans la page\ntwig de base:",[85,5849,5853],{"className":5850,"code":5851,"language":5852,"meta":42,"style":42},"language-twig shiki shiki-themes one-dark-pro","\u003Ctitle>{{ 'title' | trans }} - {% block title %}{{ 'menu.home' | trans }}{% endblock %}\u003C\u002Ftitle>\n{{ setPiwikPageName(block('title')) }}\n","twig",[81,5854,5855,5905],{"__ignoreMap":42},[93,5856,5857,5859,5862,5865,5867,5870,5873,5876,5879,5882,5885,5888,5890,5892,5895,5898,5901,5903],{"class":95,"line":96},[93,5858,177],{"class":176},[93,5860,5861],{"class":1130},"title",[93,5863,5864],{"class":176},">{{ ",[93,5866,5254],{"class":125},[93,5868,5869],{"class":176}," | ",[93,5871,5872],{"class":1130},"trans",[93,5874,5875],{"class":176}," }} - {% ",[93,5877,5878],{"class":421},"block",[93,5880,5881],{"class":1130}," title",[93,5883,5884],{"class":176}," %}{{ ",[93,5886,5887],{"class":125},"'menu.home'",[93,5889,5869],{"class":176},[93,5891,5872],{"class":1130},[93,5893,5894],{"class":176}," }}{% ",[93,5896,5897],{"class":421},"endblock",[93,5899,5900],{"class":176}," %}\u003C\u002F",[93,5902,5861],{"class":1130},[93,5904,4145],{"class":176},[93,5906,5907,5910,5912,5914,5916],{"class":95,"line":113},[93,5908,5909],{"class":176},"{{ setPiwikPageName(",[93,5911,5878],{"class":795},[93,5913,936],{"class":176},[93,5915,5254],{"class":125},[93,5917,5918],{"class":176},")) }}\n",[5920,5921,2227],"h1",{"id":2226},[11,5923,5924],{},"Bon voilà j'espère vous avoir fait découvrir le site ainsi que quelques nouveaux plugins\nintéressant.",[11,5926,5927],{},"A bientôt,",[698,5929,5930],{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .shdRp, html code.shiki .shdRp{--shiki-default:#C678DD;--shiki-default-font-style:italic}html pre.shiki code .sKU4T, html code.shiki .sKU4T{--shiki-default:#E5C07B;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":42,"searchDepth":113,"depth":113,"links":5932},[5933,5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5946,5947,5948,5949,5950],{"id":4446,"depth":119,"text":4447},{"id":4526,"depth":119,"text":4527},{"id":4549,"depth":119,"text":4522},{"id":4596,"depth":119,"text":4597},{"id":4606,"depth":113,"text":4607},{"id":5354,"depth":113,"text":5355},{"id":5364,"depth":113,"text":5365},{"id":5374,"depth":113,"text":5375},{"id":5385,"depth":113,"text":5386},{"id":5398,"depth":113,"text":5399},{"id":5405,"depth":113,"text":5406,"children":5944},[5945],{"id":5424,"depth":119,"text":5425},{"id":5434,"depth":113,"text":5435},{"id":5577,"depth":113,"text":5578},{"id":5604,"depth":113,"text":5605},{"id":5635,"depth":113,"text":5636},{"id":5665,"depth":113,"text":5666},"2014-03-19",{"type":8,"value":5953},[5954,5956,5958,5963,5968],[57,5955,4447],{"id":4446},[11,5957,746],{},[11,5959,4452,5960,4458],{},[37,5961,4457],{"href":4455,"rel":5962},[198],[11,5964,5965,4466],{},[37,5966,4465],{"href":4463,"rel":5967},[198],[11,5969,4469],{},{},"\u002Fpost\u002Fmonlivretdemesse-fr",{"title":4441,"description":42},"monlivretdemesse-fr","posts\u002FLogiciels\u002F2014-03-19-monlivretdemesse-fr",[732,5976,5977,5978],"livrets","mariage","bapteme","CLzxUTrMi7D7_9aacVnIiqCI2iUc0MClvm1Kv0yZXCA",{"id":5981,"title":5982,"author":6,"body":5983,"category":708,"categorySlug":709,"date":6956,"description":5987,"excerpt":6957,"extension":724,"location":725,"meta":6967,"navigation":267,"path":6968,"published":267,"seo":6969,"slug":6970,"stem":6971,"tags":6972,"timeToRead":271,"__hash__":6973},"posts\u002Fposts\u002FLogiciels\u002F2013-01-12-migration-to-pelican.md","Passage du site sous Pelican",{"type":8,"value":5984,"toc":6942},[5985,5988,5991,5994,5997,6001,6004,6007,6010,6013,6016,6019,6030,6033,6049,6052,6056,6059,6062,6065,6069,6072,6075,6079,6089,6095,6098,6101,6105,6112,6119,6123,6132,6135,6166,6169,6172,6189,6192,6195,6199,6202,6212,6215,6324,6327,6387,6390,6495,6512,6516,6523,6526,6544,6550,6556,6560,6563,6570,6727,6730,6733,6739,6743,6746,6749,6752,6755,6800,6810,6814,6817,6840,6850,6920,6923,6939],[11,5986,5987],{},"Bonjour à tous,",[11,5989,5990],{},"Cela fait bien longtemps que je n'ai rien écrit sur ce site. Et pour\ncause, je suis bien occupé 😄.",[11,5992,5993],{},"J'ai quand même pris un peu de temps récemment pour passer entièrement mon\nsite sous Pelican.",[11,5995,5996],{},"C'est mon cadeau de Noël de Geek ;).",[663,5998,6000],{"id":5999},"quest-ce-que-pelican","Qu'est-ce que Pelican ?",[11,6002,6003],{},"Pelican est un gestionnaire de blog statique.",[11,6005,6006],{},"Qu'est-ce que ça veut dire ?",[11,6008,6009],{},"Cela signifie que je vais écrire mes billets avec mon éditeur de texte\npréféré (kwrite), au format Markdown (ou restructuredText), et que je\ngénère mon blog au format HTML avant publication. (De la même manière que\nl'on compilerait un programme).",[11,6011,6012],{},"Le serveur n'a alors besoin de servir que des fichiers statiques, il n'y\na donc pas de surplus de mémoire, ou de délai dû à la nécessité de\ngénérer les pages.",[11,6014,6015],{},"Les pages n'étant pas modifiées tous les jours, il n'y a de toute façon\npas d'intérêt de générer les pages à chaque accès.",[11,6017,6018],{},"Les avantages indéniables sont les suivants :",[47,6020,6021,6024,6027],{},[50,6022,6023],{},"Pas de base de données, pas de page générée à la volée : donc pas de\ntemps de latence. La page est servie dés qu'elle est demandée. De plus\nil existe un plugin permettant de générer les pages au format pré-zippé,\nce qui fait un traitement de moins sur le serveur.",[50,6025,6026],{},"Pas de faille de sécurité dû à un script mal paramétré.",[50,6028,6029],{},"Je peux gérer mes billets sous un gestionnaire de version (exemple\nMercurial).",[11,6031,6032],{},"Par contre les inconvénients sont :",[47,6034,6035,6043,6046],{},[50,6036,6037,6038,1983],{},"Plus de commentaire direct sur le blog (sauf à utiliser des plateformes\ntel que ",[37,6039,6042],{"href":6040,"rel":6041},"http:\u002F\u002Fdisqus.com\u002F",[198],"disqus",[50,6044,6045],{},"Je dois installer le générateur de blog sur la\u002Fles machines que je\nsouhaite utiliser pour générer le blog.",[50,6047,6048],{},"Je dois ensuite utiliser SCP pour transférer mon blog.",[11,6050,6051],{},"Pour les deux derniers points, ils sont de mon choix. D'autres installent\nces outils sur le serveur, ils peuvent donc générer leur page directement\nà partir du serveur.",[663,6053,6055],{"id":6054},"les-implications","Les implications",[11,6057,6058],{},"Les conséquences pour ce blog sont donc : plus de commentaire (jusqu'à\nnouvel ordre). Vous pouvez toujours m'envoyer un mail, pour poser une\nquestion, je l'ajouterai manuellement en tant que commentaire.",[11,6060,6061],{},"Je garde tout de même les anciens commentaires, qui peuvent contenir\nparfois quelques informations utiles.",[11,6063,6064],{},"J'ai également profité de ce petit changement pour faire un petit\nnettoyage et j'ai supprimé tous les billets d'humeur, pour ne garder que\nles articles réellement intéressant.",[663,6066,6068],{"id":6067},"comment-sest-déroulé-la-conversion","Comment s'est déroulé la conversion ?",[11,6070,6071],{},"Dans cette partie, je vais décrire le déroulement de la conversion du\nblog.",[11,6073,6074],{},"Cette conversion comprend les articles, les commentaires et le thème.",[57,6076,6078],{"id":6077},"exportation-des-billets-provenant-de-dotclear","Exportation des billets provenant de dotclear",[11,6080,6081,6082,6084,6085,6088],{},"Il a d'abord fallu exporter les billets depuis le site sous\n",[14,6083,2830],{},". Sous Dotclear, dans la partie administration, il est possible\nd'utiliser le plugin ",[81,6086,6087],{},"Import\u002FExport"," pour exporter le site dans un fichier\ntexte.",[11,6090,6091],{},[2132,6092],{"alt":6093,"src":6094},"Export","\u002FLogiciels\u002Fmigration\u002Fdotclear_export.png",[11,6096,6097],{},"Le fichier texte se présente alors comme un mixte entre un fichier INI et\nun ensemble de fichier CSV. Il contient, les catégories, les articles, et\nles commentaires.",[11,6099,6100],{},"Ce fichier peut alors être utilisé avec l'outil d'import de pelican pour\nrécupérer les différents billets.",[57,6102,6104],{"id":6103},"création-dun-environnement-pour-pelican","Création d'un environnement pour pelican",[11,6106,6107,6108,6111],{},"La création de l'environnement se fait comme décrit sur le ",[93,6109,6110],{},"site de\npelican",". Je ne vais donc pas le décrire une fois de plus.",[11,6113,6114,6115,6118],{},"Une fois installé, les billets sont à placer dans le dossier ",[81,6116,6117],{},"content",".\nLes fichiers générés par l'import seront donc placés dans ce dossier.",[57,6120,6122],{"id":6121},"importation-des-billets-au-format-markdown","Importation des billets au format Markdown",[11,6124,6125,6126,6131],{},"L'import est décrit dans la ",[37,6127,6130],{"href":6128,"rel":6129},"http:\u002F\u002Fdocs.getpelican.com\u002Fen\u002Flatest\u002Fimporter.html",[198],"documentation de pelican",". Après différents\ntests, l'import au format Markdown a été celui qui a donné de meilleurs\nrésultats.",[11,6133,6134],{},"Voici donc la commande qui m'a permis de réaliser cet import :",[85,6136,6138],{"className":87,"code":6137,"language":89,"meta":42,"style":42},"pelican-import --dotclear -m markdown --dir-cat 2012-12-11-shadoware-backup.txt -o content\n",[81,6139,6140],{"__ignoreMap":42},[93,6141,6142,6145,6148,6151,6154,6157,6160,6163],{"class":95,"line":96},[93,6143,6144],{"class":99},"pelican-import",[93,6146,6147],{"class":103}," --dotclear",[93,6149,6150],{"class":103}," -m",[93,6152,6153],{"class":125}," markdown",[93,6155,6156],{"class":103}," --dir-cat",[93,6158,6159],{"class":125}," 2012-12-11-shadoware-backup.txt",[93,6161,6162],{"class":103}," -o",[93,6164,6165],{"class":125}," content\n",[11,6167,6168],{},"Cela m'a généré une liste de 65 fichiers : 1 par billet. Parmi ces\nbillets, j'ai fait le tri, et j'ai supprimé les billets d'humeur. J'ai\najouté également un tag indiquant l'état de draft sur quelques billets qui\nsont en cours d'écriture et que je n'ai pas encore eu le temps de\nterminer et de publier.",[11,6170,6171],{},"Puis j'ai effectué une passe sur chaque billet, pour",[47,6173,6174,6177,6180,6183,6186],{},[50,6175,6176],{},"reprendre les parties de code (coloration syntaxique),",[50,6178,6179],{},"reprendre les tableaux,",[50,6181,6182],{},"reprendre les tags (qui n'ont pas été importés),",[50,6184,6185],{},"corriger les dates (la date était celle de création du poste, et non\ncelle de publication),",[50,6187,6188],{},"modification des catégories.",[11,6190,6191],{},"Sous dotclear, j'avais fait plusieurs niveaux de catégories, alors que\nsous pelican, il n'y a qu'un seul niveau de catégorie. J'ai donc dû mettre\nà plat les différentes catégories.",[11,6193,6194],{},"Pour les tableaux, ces derniers étaient repris directement au format HTML,\nils étaient donc exploitables directement. J'ai préféré refaire les\ntableaux au format Markdown.",[57,6196,6198],{"id":6197},"ajustement-du-fichier-de-configuration","Ajustement du fichier de configuration",[11,6200,6201],{},"Viennent ensuite les ajustements faits dans le fichier de configuration,\npour que les URL des articles sous dotclear soient les plus proches\npossibles des nouvelles URL.",[11,6203,6204,6205,6208,6209,221],{},"Pour cela, j'ai choisi d'avoir les mêmes adresses que sur le site dotclear\nsuivies de ",[81,6206,6207],{},".html"," ou de ",[81,6210,6211],{},".xml",[11,6213,6214],{},"Dans le fichier, j'ai positionné les variables pelicanconf.py",[85,6216,6220],{"className":6217,"code":6218,"language":6219,"meta":42,"style":42},"language-python shiki shiki-themes one-dark-pro","ARTICLE_URL = 'post\u002F{slug}.html'\nARTICLE_SAVE_AS = 'post\u002F{slug}.html'\nAUTHOR_SAVE_AS = False\n\nFEED_DOMAIN = SITEURL\nFEED_ALL_ATOM = 'feed\u002Fatom.xml'\nCATEGORY_FEED_ATOM = 'feed\u002Fcategory\u002F%s\u002Fatom.xml'\nTAG_FEED_ATOM = 'feed\u002Ftags\u002F%s\u002Fatom.xml'\nTRANSLATION_FEED_ATOM = False\n","python",[81,6221,6222,6238,6251,6261,6265,6275,6285,6301,6315],{"__ignoreMap":42},[93,6223,6224,6227,6229,6232,6235],{"class":95,"line":96},[93,6225,6226],{"class":103},"ARTICLE_URL",[93,6228,425],{"class":795},[93,6230,6231],{"class":125}," 'post\u002F",[93,6233,6234],{"class":103},"{slug}",[93,6236,6237],{"class":125},".html'\n",[93,6239,6240,6243,6245,6247,6249],{"class":95,"line":113},[93,6241,6242],{"class":103},"ARTICLE_SAVE_AS",[93,6244,425],{"class":795},[93,6246,6231],{"class":125},[93,6248,6234],{"class":103},[93,6250,6237],{"class":125},[93,6252,6253,6256,6258],{"class":95,"line":119},[93,6254,6255],{"class":103},"AUTHOR_SAVE_AS",[93,6257,425],{"class":795},[93,6259,6260],{"class":103}," False\n",[93,6262,6263],{"class":95,"line":246},[93,6264,268],{"emptyLinePlaceholder":267},[93,6266,6267,6270,6272],{"class":95,"line":252},[93,6268,6269],{"class":103},"FEED_DOMAIN",[93,6271,425],{"class":795},[93,6273,6274],{"class":103}," SITEURL\n",[93,6276,6277,6280,6282],{"class":95,"line":258},[93,6278,6279],{"class":103},"FEED_ALL_ATOM",[93,6281,425],{"class":795},[93,6283,6284],{"class":125}," 'feed\u002Fatom.xml'\n",[93,6286,6287,6290,6292,6295,6298],{"class":95,"line":264},[93,6288,6289],{"class":103},"CATEGORY_FEED_ATOM",[93,6291,425],{"class":795},[93,6293,6294],{"class":125}," 'feed\u002Fcategory\u002F",[93,6296,6297],{"class":103},"%s",[93,6299,6300],{"class":125},"\u002Fatom.xml'\n",[93,6302,6303,6306,6308,6311,6313],{"class":95,"line":271},[93,6304,6305],{"class":103},"TAG_FEED_ATOM",[93,6307,425],{"class":795},[93,6309,6310],{"class":125}," 'feed\u002Ftags\u002F",[93,6312,6297],{"class":103},[93,6314,6300],{"class":125},[93,6316,6317,6320,6322],{"class":95,"line":277},[93,6318,6319],{"class":103},"TRANSLATION_FEED_ATOM",[93,6321,425],{"class":795},[93,6323,6260],{"class":103},[11,6325,6326],{},"Dans le fichier, j'ai positionné les variables publishconf.py",[85,6328,6330],{"className":6217,"code":6329,"language":6219,"meta":42,"style":42},"ARTICLE_URL = 'post\u002F{slug}'\nPAGE_URL = 'pages\u002F{slug}'\nCATEGORY_URL = 'category\u002F{slug}'\nTAG_URL = 'tag\u002F{slug}'\n",[81,6331,6332,6345,6359,6373],{"__ignoreMap":42},[93,6333,6334,6336,6338,6340,6342],{"class":95,"line":96},[93,6335,6226],{"class":103},[93,6337,425],{"class":795},[93,6339,6231],{"class":125},[93,6341,6234],{"class":103},[93,6343,6344],{"class":125},"'\n",[93,6346,6347,6350,6352,6355,6357],{"class":95,"line":113},[93,6348,6349],{"class":103},"PAGE_URL",[93,6351,425],{"class":795},[93,6353,6354],{"class":125}," 'pages\u002F",[93,6356,6234],{"class":103},[93,6358,6344],{"class":125},[93,6360,6361,6364,6366,6369,6371],{"class":95,"line":119},[93,6362,6363],{"class":103},"CATEGORY_URL",[93,6365,425],{"class":795},[93,6367,6368],{"class":125}," 'category\u002F",[93,6370,6234],{"class":103},[93,6372,6344],{"class":125},[93,6374,6375,6378,6380,6383,6385],{"class":95,"line":246},[93,6376,6377],{"class":103},"TAG_URL",[93,6379,425],{"class":795},[93,6381,6382],{"class":125}," 'tag\u002F",[93,6384,6234],{"class":103},[93,6386,6344],{"class":125},[11,6388,6389],{},"Du coté de nginx, le serveur d'application, j'ai ajouté les lignes\nsuivantes :",[85,6391,6395],{"className":6392,"code":6393,"language":6394,"meta":42,"style":42},"language-nginx shiki shiki-themes one-dark-pro","rewrite ^\u002Fdotclear\u002Findex.php\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$ \u002Fpost\u002F$1 permanent;\nrewrite ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$ \u002Fpost\u002F$1 permanent;\nrewrite ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F(.*)$ \u002Fpost\u002F$1 permanent;\n\nlocation \u002F {\n    try_files $uri.html $uri.xml $uri $uri\u002F =404;\n}\n","nginx",[81,6396,6397,6415,6430,6445,6449,6457,6491],{"__ignoreMap":42},[93,6398,6399,6402,6405,6408,6410,6413],{"class":95,"line":96},[93,6400,6401],{"class":421},"rewrite",[93,6403,6404],{"class":1130}," ^\u002Fdotclear\u002Findex.php\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$",[93,6406,6407],{"class":176}," \u002Fpost\u002F$",[93,6409,44],{"class":1130},[93,6411,6412],{"class":421}," permanent",[93,6414,2600],{"class":176},[93,6416,6417,6419,6422,6424,6426,6428],{"class":95,"line":113},[93,6418,6401],{"class":421},[93,6420,6421],{"class":1130}," ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$",[93,6423,6407],{"class":176},[93,6425,44],{"class":1130},[93,6427,6412],{"class":421},[93,6429,2600],{"class":176},[93,6431,6432,6434,6437,6439,6441,6443],{"class":95,"line":119},[93,6433,6401],{"class":421},[93,6435,6436],{"class":1130}," ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F(.*)$",[93,6438,6407],{"class":176},[93,6440,44],{"class":1130},[93,6442,6412],{"class":421},[93,6444,2600],{"class":176},[93,6446,6447],{"class":95,"line":246},[93,6448,268],{"emptyLinePlaceholder":267},[93,6450,6451,6454],{"class":95,"line":252},[93,6452,6453],{"class":421},"location",[93,6455,6456],{"class":176}," \u002F {\n",[93,6458,6459,6462,6465,6468,6471,6473,6476,6478,6481,6483,6486,6489],{"class":95,"line":258},[93,6460,6461],{"class":421},"    try_files ",[93,6463,6464],{"class":176},"$",[93,6466,6467],{"class":1130},"uri",[93,6469,6470],{"class":176},".html $",[93,6472,6467],{"class":1130},[93,6474,6475],{"class":176},".xml $",[93,6477,6467],{"class":1130},[93,6479,6480],{"class":176}," $",[93,6482,6467],{"class":1130},[93,6484,6485],{"class":176},"\u002F ",[93,6487,6488],{"class":103},"=404",[93,6490,2600],{"class":176},[93,6492,6493],{"class":95,"line":264},[93,6494,2101],{"class":176},[11,6496,6497,6498,6501,6502,6505,6506,6508,6509,6511],{},"Les premières lignes permettent de reprendre les anciennes adresses\n",[81,6499,6500],{},"dotclear",", et la commande ",[81,6503,6504],{},"try_files"," permet de reprendre les adresses\nexistantes et de les compléter par ",[81,6507,6207],{}," et par ",[81,6510,6211],{}," selon le besoin.",[57,6513,6515],{"id":6514},"modification-du-thème","Modification du thème",[11,6517,6518,6519,6522],{},"Enfin j'ai écrit un nouveau thème dans le dossier ",[81,6520,6521],{},"themes\u002Fshadoware",". Ce\nthème est fait pour coller le plus possible à celui que j'avais sur le\nsite dotclear.",[11,6524,6525],{},"Il génère donc la page de la même manière que dotclear le faisait (à\nquelques différences prés) pour éviter de devoir ré-écrire la feuille de\nstyle CSS.",[11,6527,6528,6529,6534,6535,464,6538,6541,6542,221],{},"L'écriture du thème n'est en soit pas très compliqué, car ",[37,6530,6533],{"href":6531,"rel":6532},"http:\u002F\u002Fjinja.pocoo.org\u002Fdocs\u002F",[198],"jinja2"," est\nassez simple d'utilisation. Je me suis inspiré des thèmes ",[81,6536,6537],{},"simple",[81,6539,6540],{},"notmyidea"," pour écrire le thème, basé sur celui que j'avais sous\n",[81,6543,6500],{},[11,6545,6546,6547,221],{},"Vous pouvez trouver le thème au ",[93,6548,6549],{},"lien suivant",[11,6551,6552],{},[2132,6553],{"alt":6554,"src":6555},"Theme","\u002FLogiciels\u002Fmigration\u002Fsite.png",[57,6557,6559],{"id":6558},"importation-des-commentaires","Importation des commentaires",[11,6561,6562],{},"Les commentaires existants sur mon blog actuel peuvent parfois contenir\ndes informations complémentaires. Les ignorer lors de la migration est\ntout a fait possible, mais j'ai préféré les récupérer.",[11,6564,6565,6566,6569],{},"Pour cela j'ai installé le ",[93,6567,6568],{},"plugin comments",". Ensuite j'ai modifié le\nthème pour ajouter l'affichage des commentaires :",[85,6571,6573],{"className":4131,"code":6572,"language":4133,"meta":42,"style":42},"{% if article.comments %}\n\u003Cdiv id=\"comments\">\n  \u003Ch3>Commentaires\u003C\u002Fh3>\n  \u003Cdl>\n    {% for comment in article.comments %}\n    \u003Cdt>\n      \u003Ca class=\"comment-number\" href=\"#\">{{ loop.index }}.\u003C\u002Fa>\n      Le {{ comment.date }} par {{ comment.author }}\n    \u003C\u002Fdt>\n    \u003Cdd>\n      \u003Cp>{{ comment.content }}\u003C\u002Fp>\n    \u003C\u002Fdd>\n    {% endfor %}\n  \u003C\u002Fdl>\n\u003C\u002Fdiv>\n{% endif %}\n",[81,6574,6575,6580,6594,6607,6616,6621,6630,6658,6663,6671,6680,6693,6701,6706,6714,6722],{"__ignoreMap":42},[93,6576,6577],{"class":95,"line":96},[93,6578,6579],{"class":176},"{% if article.comments %}\n",[93,6581,6582,6584,6586,6588,6590,6592],{"class":95,"line":113},[93,6583,177],{"class":176},[93,6585,4168],{"class":1130},[93,6587,4188],{"class":103},[93,6589,1074],{"class":176},[93,6591,4193],{"class":125},[93,6593,4145],{"class":176},[93,6595,6596,6598,6600,6603,6605],{"class":95,"line":119},[93,6597,4155],{"class":176},[93,6599,57],{"class":1130},[93,6601,6602],{"class":176},">Commentaires\u003C\u002F",[93,6604,57],{"class":1130},[93,6606,4145],{"class":176},[93,6608,6609,6611,6614],{"class":95,"line":246},[93,6610,4155],{"class":176},[93,6612,6613],{"class":1130},"dl",[93,6615,4145],{"class":176},[93,6617,6618],{"class":95,"line":252},[93,6619,6620],{"class":176},"    {% for comment in article.comments %}\n",[93,6622,6623,6625,6628],{"class":95,"line":258},[93,6624,4165],{"class":176},[93,6626,6627],{"class":1130},"dt",[93,6629,4145],{"class":176},[93,6631,6632,6634,6636,6638,6640,6643,6646,6648,6651,6654,6656],{"class":95,"line":264},[93,6633,4183],{"class":176},[93,6635,37],{"class":1130},[93,6637,4171],{"class":103},[93,6639,1074],{"class":176},[93,6641,6642],{"class":125},"\"comment-number\"",[93,6644,6645],{"class":103}," href",[93,6647,1074],{"class":176},[93,6649,6650],{"class":125},"\"#\"",[93,6652,6653],{"class":176},">{{ loop.index }}.\u003C\u002F",[93,6655,37],{"class":1130},[93,6657,4145],{"class":176},[93,6659,6660],{"class":95,"line":271},[93,6661,6662],{"class":176},"      Le {{ comment.date }} par {{ comment.author }}\n",[93,6664,6665,6667,6669],{"class":95,"line":277},[93,6666,4286],{"class":176},[93,6668,6627],{"class":1130},[93,6670,4145],{"class":176},[93,6672,6673,6675,6678],{"class":95,"line":283},[93,6674,4165],{"class":176},[93,6676,6677],{"class":1130},"dd",[93,6679,4145],{"class":176},[93,6681,6682,6684,6686,6689,6691],{"class":95,"line":288},[93,6683,4183],{"class":176},[93,6685,11],{"class":1130},[93,6687,6688],{"class":176},">{{ comment.content }}\u003C\u002F",[93,6690,11],{"class":1130},[93,6692,4145],{"class":176},[93,6694,6695,6697,6699],{"class":95,"line":293},[93,6696,4286],{"class":176},[93,6698,6677],{"class":1130},[93,6700,4145],{"class":176},[93,6702,6703],{"class":95,"line":298},[93,6704,6705],{"class":176},"    {% endfor %}\n",[93,6707,6708,6710,6712],{"class":95,"line":1114},[93,6709,4295],{"class":176},[93,6711,6613],{"class":1130},[93,6713,4145],{"class":176},[93,6715,6716,6718,6720],{"class":95,"line":1176},[93,6717,4304],{"class":176},[93,6719,4168],{"class":1130},[93,6721,4145],{"class":176},[93,6723,6724],{"class":95,"line":1181},[93,6725,6726],{"class":176},"{% endif %}\n",[11,6728,6729],{},"Cela permet d'afficher les commentaires à l'aide du plugin. Par contre le\nproblème est que les commentaires provenant de dotclear ne sont pas\nimportés.",[11,6731,6732],{},"Du coup j'ai modifié le script d'import, pour en faire un qui importe les\ncommentaires. Il doit être lancé séparément du script d'import\ntraditionnel.",[11,6734,6735,6738],{},[93,6736,6737],{},"Voici donc le script"," permettant de récupérer les commentaires.",[57,6740,6742],{"id":6741},"gestion-du-tag-planet-libre","Gestion du tag planet libre",[11,6744,6745],{},"Afin de pouvoir publier des articles sur le planet libre, je proposais\njusqu'ici des articles sur le planet libre à l'aide d'un tag de type\nplanet.",[11,6747,6748],{},"L'utilisation d'un tag de type planet permet de filtrer les articles\nque je souhaite mettre sur le planet libre. Par contre avec pelican,\nl'utilisation de ce tag fausse le calcul des billets liés.",[11,6750,6751],{},"Du coup j'ai décidé que la génération du flux ATOM donné au planet sera\ngénérée avec les articles qui posséderont un metatag déposé au début de\nchaque billet. Sous la présence du tag avec la valeur true, le billet est\najouté dans le flux ATOM des planets.",[11,6753,6754],{},"Pour ajouter le tag, il faut donc au début de l'article écrire le texte\nsuivant :",[85,6756,6760],{"className":6757,"code":6758,"language":6759,"meta":42,"style":42},"language-markdown shiki shiki-themes one-dark-pro","Title: Passage du site sous Pelican\nTags: dedie\nPlanet: true\n\nBonjour à tous,\n\nCela fait bien longtemps que je n'ai rien écrit sur ce site. Et pour\ncause, je suis bien occupé 😄.\n","markdown",[81,6761,6762,6767,6772,6777,6781,6786,6790,6795],{"__ignoreMap":42},[93,6763,6764],{"class":95,"line":96},[93,6765,6766],{"class":176},"Title: Passage du site sous Pelican\n",[93,6768,6769],{"class":95,"line":113},[93,6770,6771],{"class":176},"Tags: dedie\n",[93,6773,6774],{"class":95,"line":119},[93,6775,6776],{"class":176},"Planet: true\n",[93,6778,6779],{"class":95,"line":246},[93,6780,268],{"emptyLinePlaceholder":267},[93,6782,6783],{"class":95,"line":252},[93,6784,6785],{"class":176},"Bonjour à tous,\n",[93,6787,6788],{"class":95,"line":258},[93,6789,268],{"emptyLinePlaceholder":267},[93,6791,6792],{"class":95,"line":264},[93,6793,6794],{"class":176},"Cela fait bien longtemps que je n'ai rien écrit sur ce site. Et pour\n",[93,6796,6797],{"class":95,"line":271},[93,6798,6799],{"class":176},"cause, je suis bien occupé 😄.\n",[11,6801,6802,6803,6806,6807,221],{},"Pour ce faire j'ai écrit une extension à pelican qui génére un flux nommé\n",[81,6804,6805],{},"{OUTPUT_PATH}\u002F{TAG_FEED_ATOM}"," (avec %s remplacé par planet). Vous pouvez\ntélécharger l'",[93,6808,6809],{},"extension au lien suivant",[57,6811,6813],{"id":6812},"création-dune-page-404","Création d'une page 404",[11,6815,6816],{},"Afin d'avoir une belle page 404 si la page n'est pas correctement trouvée\n(par exemple si certaines URLs ne correspondent plus à celle de dotclear),\nj'ai défini au niveau de pelican, la création d'une belle page 404 de la\nmanière suivante :",[85,6818,6820],{"className":6217,"code":6819,"language":6219,"meta":42,"style":42},"TEMPLATE_PAGES = {'error404.html': 'error404.html'}\n",[81,6821,6822],{"__ignoreMap":42},[93,6823,6824,6827,6829,6831,6834,6836,6838],{"class":95,"line":96},[93,6825,6826],{"class":103},"TEMPLATE_PAGES",[93,6828,425],{"class":795},[93,6830,1567],{"class":176},[93,6832,6833],{"class":125},"'error404.html'",[93,6835,5701],{"class":176},[93,6837,6833],{"class":125},[93,6839,2101],{"class":176},[11,6841,6842,6843,6845,6846,6849],{},"J'ai alors créé dans le dossier ",[81,6844,6117],{}," le fichier ",[81,6847,6848],{},"error404.html"," avec\nle contenu suivant :",[85,6851,6854],{"className":6852,"code":6853,"language":6533,"meta":42,"style":42},"language-jinja2 shiki shiki-themes one-dark-pro","{% extends \"base.html\" %}\n{% block title %}Page non trouvée{% endblock %}\n{% block piwik_title %}404{% endblock %}\n{% block content %}\n\u003Csection id=\"content\">\n    \u003Cdiv id=\"content-info\">\n        \u003Ch2>Document non trouvé\u003C\u002Fh2>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"content-inner\">\n        \u003Cp>Le document que vous cherchez n'existe pas.\u003C\u002Fp>\n    \u003C\u002Fdiv>\n\u003C\u002Fsection>\n{% endblock %}\n",[81,6855,6856,6861,6866,6871,6876,6881,6886,6891,6896,6901,6906,6910,6915],{"__ignoreMap":42},[93,6857,6858],{"class":95,"line":96},[93,6859,6860],{},"{% extends \"base.html\" %}\n",[93,6862,6863],{"class":95,"line":113},[93,6864,6865],{},"{% block title %}Page non trouvée{% endblock %}\n",[93,6867,6868],{"class":95,"line":119},[93,6869,6870],{},"{% block piwik_title %}404{% endblock %}\n",[93,6872,6873],{"class":95,"line":246},[93,6874,6875],{},"{% block content %}\n",[93,6877,6878],{"class":95,"line":252},[93,6879,6880],{},"\u003Csection id=\"content\">\n",[93,6882,6883],{"class":95,"line":258},[93,6884,6885],{},"    \u003Cdiv id=\"content-info\">\n",[93,6887,6888],{"class":95,"line":264},[93,6889,6890],{},"        \u003Ch2>Document non trouvé\u003C\u002Fh2>\n",[93,6892,6893],{"class":95,"line":271},[93,6894,6895],{},"    \u003C\u002Fdiv>\n",[93,6897,6898],{"class":95,"line":277},[93,6899,6900],{},"    \u003Cdiv class=\"content-inner\">\n",[93,6902,6903],{"class":95,"line":283},[93,6904,6905],{},"        \u003Cp>Le document que vous cherchez n'existe pas.\u003C\u002Fp>\n",[93,6907,6908],{"class":95,"line":288},[93,6909,6895],{},[93,6911,6912],{"class":95,"line":293},[93,6913,6914],{},"\u003C\u002Fsection>\n",[93,6916,6917],{"class":95,"line":298},[93,6918,6919],{},"{% endblock %}\n",[11,6921,6922],{},"Enfin, comme j'utilise le serveur nginx pour servir mes pages j'ai ajouté\nla règle (toute simple) suivante pour définir ma page 404 :",[85,6924,6926],{"className":6392,"code":6925,"language":6394,"meta":42,"style":42},"error_page 404 \u002Ferror404.html;\n",[81,6927,6928],{"__ignoreMap":42},[93,6929,6930,6933,6936],{"class":95,"line":96},[93,6931,6932],{"class":421},"error_page ",[93,6934,6935],{"class":103},"404",[93,6937,6938],{"class":176}," \u002Ferror404.html;\n",[698,6940,6941],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}",{"title":42,"searchDepth":113,"depth":113,"links":6943},[6944,6945,6946],{"id":5999,"depth":113,"text":6000},{"id":6054,"depth":113,"text":6055},{"id":6067,"depth":113,"text":6068,"children":6947},[6948,6949,6950,6951,6952,6953,6954,6955],{"id":6077,"depth":119,"text":6078},{"id":6103,"depth":119,"text":6104},{"id":6121,"depth":119,"text":6122},{"id":6197,"depth":119,"text":6198},{"id":6514,"depth":119,"text":6515},{"id":6558,"depth":119,"text":6559},{"id":6741,"depth":119,"text":6742},{"id":6812,"depth":119,"text":6813},"2013-01-12",{"type":8,"value":6958},[6959,6961,6963,6965],[11,6960,5987],{},[11,6962,5990],{},[11,6964,5993],{},[11,6966,5996],{},{"planet":267},"\u002Fpost\u002Fmigration-to-pelican",{"title":5982,"description":5987},"migration-to-pelican","posts\u002FLogiciels\u002F2013-01-12-migration-to-pelican",[732],"RjPEE1XKQzoFIQDsH16W_XStvfsUCDU0KyWZDWKPIXs",1777849590731]