Arrivano i moduli                 come li sfruttiamo?Zend Framework Day – Milano – 01/02/2013
@stefanovalle
http://www.mvassociati.it/
http://friuli.grusp.org/
I MODULI DI ZF2
I moduli, come li conosciamo in ZF1                          Sito Web      contenuti statici, catalogo servizi, form conta...
I moduli, come li conosciamo in ZF1                             Sito Web        contenuti statici, catalogo servizi, form ...
I moduli, come li conosciamo in ZF1                             Sito Web        contenuti statici, catalogo servizi, form ...
E IN ZF2?
A re-usable piece of     functionality that can be used      to construct a more complex               application.       ...
A re-usable piece of     functionality that can be used      to construct a more complex               application.       ...
Cosa posso riutilizzare?                                Sito Web           contenuti statici, catalogo servizi, form conta...
Cosa posso riutilizzare?                                Sito Web           contenuti statici, catalogo servizi, form conta...
Pensiamo alle funzionalità                                   Sito Web       Contenuti       e pagine                     M...
Pensiamo alle funzionalità                                               Sito Web       Contenuti                   Catalo...
Ecco i moduli!                                               Sito Web       Contenuti                   Catalogo    Form  ...
OK, MA IN CONCRETO?
Namespace   Module.php   Modulo18
Namespace         Module.php       Modulo                 Namespace MioModulo;                 Class Module {}19
Cosa possiamo farci?      Formattare una     Mostrare una          valuta       form di contatto20
Cosa possiamo farci?      Formattare una       Mostrare una          valuta         form di contatto      Gestire utenti e...
Cosa possiamo farci?      Formattare una       Mostrare una          valuta         form di contatto      Gestire utenti e...
Cosa possiamo farci?      Formattare una       Mostrare una          valuta         form di contatto      Gestire utenti e...
PROVIAMO A CAPIRCI QUALCOSA
Dove si trovano?                   Moduli dell’applicazione                    Moduli di terze parti25
Da dove partiamo?     http://modules.zendframework.com/26
Com’è fatto un modulo                 configurazioni27
File di configurazione// config/module.config.phpreturn array(  router => array(    routes => array(...)   ),    controlle...
Com’è fatto un modulo                 file PHP del modulo                 (Controller, Form, Modello, Servizi, ecc.)29
Com’è fatto un modulo                 File per il test del modulo30
Com’è fatto un modulo                 tutto ciò che riguarda le viste                 (pagine, layout, partials)31
Attivazione di un modulo     config/application.config.php     return array(       modules => array(          DoctrineModu...
Attivazione di un modulo     config/application.config.php     return array(       modules => array(          DoctrineModu...
MERGE DELLA CONFIGURAZIONE
Posso fare l’override…     …di viste / layout   return array(                            ...                            vi...
Posso fare l’override…     …di viste / layout   return array(                            ...     …del routing           ro...
Posso fare l’override…     …di viste / layout     …del routing     …di tutte le configurazioni37
Posso fare l’override…     …di viste / layout     …del routing     …di tutte le configurazioni                            ...
RIUSABILI
Realizziamo un E-Commerce40
DIAMO UN’OCCHIATA IN GIRO…
DUBBI INCONTRATI
1 - COME ORGANIZZO I MODULI?
Di cosa ho bisogno?44
Di cosa ho bisogno?     •   Catalogo prodotti     •   Carrello     •   Checkout (e pagamento)     •   Pagine (più o meno s...
Di cosa ho bisogno?     •   Catalogo prodotti     •   Carrello                       Un modulo     •   Checkout (e pagamen...
Di cosa ho bisogno?     •   Catalogo prodotti     •   Carrello                                       Un modulo     •   Che...
2) IMMAGINI/JS/CSS
Condivisi     Librerie javascript, loghi, pulsanti, CSS, ecc.49
Condivisi     Librerie javascript, loghi, pulsanti, CSS, ecc.     Dove li mettiamo?50
Condivisi     Librerie javascript, loghi, pulsanti, CSS, ecc.     Dove li mettiamo?     Dentro cartella /public?51
Esclusivi     Librerie javascript, immagini e CSS che     servono solo ad un singolo modulo52
Esclusivi     Librerie javascript, immagini e CSS che     servono solo ad un singolo modulo     Dove li mettiamo?53
Esclusivi     Librerie javascript, immagini e CSS che     servono solo ad un singolo modulo     Dove li mettiamo?     Dent...
Soluzioni?     •   Copia & incolla dei file dentro la cartella         public?     •   Symlinks?     •   Configurare apach...
Meglio: modulo AssetManager56
Esistono anche alternative, ad     esempio…57
Modulo AssetManager          Assets                    Asset                   Manager58
Resolvers     Assets               Asset      Filters              Manager59
Resolvers     Assets               Asset      Filters              Manager               Cache60
Dove mettiamo gli asset?                  tutti gli asset                   del modulo61
module.config.phpreturn array(  asset_manager => array(    resolver_configs => array(      paths => array(        __DIR__ ...
module.config.phpreturn array(  asset_manager => array(    resolver_configs => array(      paths => array(        __DIR__ ...
module.config.phpreturn array(asset_manager => array(  resolver_configs => array(    map => array(       css/primo.css => ...
module.config.phpreturn array(asset_manager => array(  resolver_configs => array(    map => array(       css/primo.css => ...
Ora il modulo è davvero             riusabile!66
3) ENTITA’ CONDIVISE
Catalogo   Carrello68
Catalogo                    Carrello                  moduli                Prodotto            (con la sua entità del    ...
Catalogo              Carrello                  moduli                Prodotto           - id           - nome           -...
Catalogo              Carrello          ?                ?                Prodotto           - id           - nome        ...
Modulo «carrello», vorrei:     •   Vederlo all’opera standalone,         senza altre dipendenze72
Modulo «carrello», vorrei:     •   Vederlo all’opera standalone,         senza altre dipendenze     •   Poter gestire prod...
Modulo «carrello», vorrei:     •   Vederlo all’opera standalone,         senza altre dipendenze     •   Poter gestire prod...
Il carrello vuole un prodotto con     certe caratteristiche...75
Carrello        Prodotto     - id     - nome     - prezzo76
Catalogo              Carrello          Prodotto              Prodotto     - id                    - id     - nome        ...
Catalogo            Carrello          Prodotto     - id     - nome     - prezzo     -   dimensioneSchermo     -   RAM     ...
Come? Con il Service Manager/* Modulo Carrello – module.config.php */return array(   router => array(…),   controllers => ...
ProductService/* Modulo Carrello – EcommerceServiceProductService */class ProductService implements ... {    public functi...
Nel modulo «catalogo»? Override!/* Modulo Catalogo – module.config.php */return array(   router => array(…),   controllers...
Nel modulo «catalogo»? Override!/* Modulo Catalogo – module.config.php */return array(   router => array(…),   controllers...
E i «vincoli» (es. nome + prezzo)?
E i «vincoli» (es. nome + prezzo)?Ereditarietà di classe/** * CatalogEntityProduct * ...*/class Product extends EcommerceE...
Catalogo             Carrello       Prodotto      id, nome, prezzo     dimensioneSchermo,     RAM, prezzoOfferta,         ...
Ora il modulo è davvero             riusabile!86
PROBLEMA 4: PANNELLO ADMIN
Ho bisogno     di un’altra cartella          /public?88
Ho bisogno     di un’altra cartella          /public?     Probabilmente no      (a meno di casi particolari)89
Creo un modulo per tutta la        parte amministrativa?90
Creo un modulo per tutta la        parte amministrativa?             Potrei…      ma non sarebbe riusabile           (né l...
Inglobo la parte amministrativa         dentro ciascun modulo?92
Inglobo la parte amministrativa         dentro ciascun modulo?                     Esatto!          O almeno nella maggior...
Da dove eravamo partiti…                                               Sito Web       Contenuti                   Catalogo...
Ciò che voglio:     http://mio.si.to/admin     http://mio.si.to/admin/catalog     http://mio.si.to/admin/user     http://m...
Come?       Un modulo di96
1. Attivazione modulo/* application.config.php */return array(  modules => array(…),     ZfcAdmin,     Application,  ),);
2. Creazione controller98
3. Configurazione routing/* Modulo Catalog – module.config.php */return array(   router => array(      zfcadmin => array( ...
3. Configurazione routing/* Modulo Catalog – module.config.php */return array(   router => array(      zfcadmin => array( ...
3. Configurazione routing/* Modulo Catalog – module.config.php */return array(   router => array(      zfcadmin => array( ...
Il pannello admin del         modulo è pronto!102
Il pannello admin del         modulo è pronto!          e l’autenticazione?103
Autenticazione                   +104
Gestione autorizzazioni/* Modulo AppCatalog – module.config.php */return array(  …  bjyauthorize => array(     guards => a...
Tiriamo le somme106
+ RIUSO
APPLICAZIONI CUSTOM
Grazie per l’attenzioneStefano Valle@stefanovalles.valle@mvassociati.it
DOMANDE?
Photo Credits      •   http://www.flickr.com/photos/10459273@N05/4771563267      •   http://www.flickr.com/photos/vittorio...
Moduli su Zend Framework 2: come sfruttarli
Moduli su Zend Framework 2: come sfruttarli
Upcoming SlideShare
Loading in...5
×

Moduli su Zend Framework 2: come sfruttarli

2,301

Published on

Presentazione sui moduli introdotti in Zend Framework 2, tenuta allo Zend Framework Day del 01/02/2013 a Milano.

Come organizzo il mio modello, se le entità di base sono condivise tra più moduli? La mia applicazione ha anche un pannello amministrativo: dove metto viste e file del front-end? Come gestisco le eventuali dipendenze esterne? In questo talk cerco di rispondere a queste e altre domande relative all'organizzazione di un progetto ZF2, condividendo la mia esperienza pratica, i dubbi che mi sono posto, e le soluzioni che ho adottato. Non sempre le risposte sono state scontate, a maggior ragione dato che - quale sviluppatore ZF1 - non ero abituato all'uso dei moduli - così come intesi in ZF2. L'introduzione degli stessi può rappresentare una vera e propria svolta; è però fondamentale organizzare opportunamente il proprio progetto, dato che le reali possibilità di riuso dipendono in buona parte proprio da una buona organizzazione del proprio codice.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,301
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
56
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Moduli su Zend Framework 2: come sfruttarli

  1. 1. Arrivano i moduli come li sfruttiamo?Zend Framework Day – Milano – 01/02/2013
  2. 2. @stefanovalle
  3. 3. http://www.mvassociati.it/
  4. 4. http://friuli.grusp.org/
  5. 5. I MODULI DI ZF2
  6. 6. I moduli, come li conosciamo in ZF1 Sito Web contenuti statici, catalogo servizi, form contatti, ecc.6
  7. 7. I moduli, come li conosciamo in ZF1 Sito Web contenuti statici, catalogo servizi, form contatti, ecc. Amministrazione sito Web gestione contenuti statici, catalogo servizi, form contatti, ecc.7
  8. 8. I moduli, come li conosciamo in ZF1 Sito Web contenuti statici, catalogo servizi, form contatti, ecc. Modello Amministrazione sito Web gestione contenuti statici, catalogo servizi, form contatti, ecc.8
  9. 9. E IN ZF2?
  10. 10. A re-usable piece of functionality that can be used to construct a more complex application. Evan Coury - http://evan.pro/zf2-modules-talk.html10
  11. 11. A re-usable piece of functionality that can be used to construct a more complex application. Evan Coury - http://evan.pro/zf2-modules-talk.html11
  12. 12. Cosa posso riutilizzare? Sito Web contenuti statici, catalogo servizi, form contatti, ecc. Modello Amministrazione sito Web gestione contenuti statici, catalogo servizi, form contatti, ecc.12
  13. 13. Cosa posso riutilizzare? Sito Web contenuti statici, catalogo servizi, form contatti, ecc. Modello Amministrazione sito Web gestione contenuti statici, catalogo servizi, form contatti, ecc.13
  14. 14. Pensiamo alle funzionalità Sito Web Contenuti e pagine Modello statiche Amministrazione sito Web14
  15. 15. Pensiamo alle funzionalità Sito Web Contenuti Catalogo Form e pagine servizi contatti Modello statiche Amministrazione sito Web15
  16. 16. Ecco i moduli! Sito Web Contenuti Catalogo Form e pagine servizi contatti Modello statiche Amministrazione sito Web16
  17. 17. OK, MA IN CONCRETO?
  18. 18. Namespace Module.php Modulo18
  19. 19. Namespace Module.php Modulo Namespace MioModulo; Class Module {}19
  20. 20. Cosa possiamo farci? Formattare una Mostrare una valuta form di contatto20
  21. 21. Cosa possiamo farci? Formattare una Mostrare una valuta form di contatto Gestire utenti e Integrare un ACL ORM21
  22. 22. Cosa possiamo farci? Formattare una Mostrare una valuta form di contatto Gestire utenti e Integrare un ACL ORM Inglobare tutta l’applicazione22
  23. 23. Cosa possiamo farci? Formattare una Mostrare una valuta form di contatto Gestire utenti e Integrare un ACL ORM Inglobare tutta l’applicazione23
  24. 24. PROVIAMO A CAPIRCI QUALCOSA
  25. 25. Dove si trovano? Moduli dell’applicazione Moduli di terze parti25
  26. 26. Da dove partiamo? http://modules.zendframework.com/26
  27. 27. Com’è fatto un modulo configurazioni27
  28. 28. File di configurazione// config/module.config.phpreturn array( router => array( routes => array(...) ), controllers => array(...), service_manager => array(...), view_manager => array(...), ...)
  29. 29. Com’è fatto un modulo file PHP del modulo (Controller, Form, Modello, Servizi, ecc.)29
  30. 30. Com’è fatto un modulo File per il test del modulo30
  31. 31. Com’è fatto un modulo tutto ciò che riguarda le viste (pagine, layout, partials)31
  32. 32. Attivazione di un modulo config/application.config.php return array( modules => array( DoctrineModule, DoctrineORMModule, SpeckPaypal, Application, ), ), ...32
  33. 33. Attivazione di un modulo config/application.config.php return array( modules => array( DoctrineModule, DoctrineORMModule, SpeckPaypal, Attenzione Application, all’ordinamento ), ), ...33
  34. 34. MERGE DELLA CONFIGURAZIONE
  35. 35. Posso fare l’override… …di viste / layout return array( ... view_manager => array( ... ), )35
  36. 36. Posso fare l’override… …di viste / layout return array( ... …del routing router => array( ... ), )36
  37. 37. Posso fare l’override… …di viste / layout …del routing …di tutte le configurazioni37
  38. 38. Posso fare l’override… …di viste / layout …del routing …di tutte le configurazioni estendendo, non modificando il modulo originale38
  39. 39. RIUSABILI
  40. 40. Realizziamo un E-Commerce40
  41. 41. DIAMO UN’OCCHIATA IN GIRO…
  42. 42. DUBBI INCONTRATI
  43. 43. 1 - COME ORGANIZZO I MODULI?
  44. 44. Di cosa ho bisogno?44
  45. 45. Di cosa ho bisogno? • Catalogo prodotti • Carrello • Checkout (e pagamento) • Pagine (più o meno statiche) • Pannello amministrativo45
  46. 46. Di cosa ho bisogno? • Catalogo prodotti • Carrello Un modulo • Checkout (e pagamento) ciascuno • Pagine (più o meno statiche) • Pannello amministrativo46
  47. 47. Di cosa ho bisogno? • Catalogo prodotti • Carrello Un modulo • Checkout (e pagamento) ciascuno • Pagine (più o meno statiche) • Pannello amministrativo «orizzontale» rispetto agli altri moduli (lasciamolo un attimo in sospeso…)47
  48. 48. 2) IMMAGINI/JS/CSS
  49. 49. Condivisi Librerie javascript, loghi, pulsanti, CSS, ecc.49
  50. 50. Condivisi Librerie javascript, loghi, pulsanti, CSS, ecc. Dove li mettiamo?50
  51. 51. Condivisi Librerie javascript, loghi, pulsanti, CSS, ecc. Dove li mettiamo? Dentro cartella /public?51
  52. 52. Esclusivi Librerie javascript, immagini e CSS che servono solo ad un singolo modulo52
  53. 53. Esclusivi Librerie javascript, immagini e CSS che servono solo ad un singolo modulo Dove li mettiamo?53
  54. 54. Esclusivi Librerie javascript, immagini e CSS che servono solo ad un singolo modulo Dove li mettiamo? Dentro ciascun modulo!54
  55. 55. Soluzioni? • Copia & incolla dei file dentro la cartella public? • Symlinks? • Configurare apache per «cercare» i file fra i vari moduli?55
  56. 56. Meglio: modulo AssetManager56
  57. 57. Esistono anche alternative, ad esempio…57
  58. 58. Modulo AssetManager Assets Asset Manager58
  59. 59. Resolvers Assets Asset Filters Manager59
  60. 60. Resolvers Assets Asset Filters Manager Cache60
  61. 61. Dove mettiamo gli asset? tutti gli asset del modulo61
  62. 62. module.config.phpreturn array( asset_manager => array( resolver_configs => array( paths => array( __DIR__ . /../assets, ),),),), ...
  63. 63. module.config.phpreturn array( asset_manager => array( resolver_configs => array( paths => array( __DIR__ . /../assets, ),),),), ... layout.php echo $this->headLink() ->prependStylesheet($this->basePath() . /css/miocss.css);
  64. 64. module.config.phpreturn array(asset_manager => array( resolver_configs => array( map => array( css/primo.css => __DIR__./../assets/css/primo.css, css/secondo.css => __DIR__./../assets/css/secondo.css, ), collections => array( css/merge.css => array( css/primo.css,css/secondo.css,),),),),), ...
  65. 65. module.config.phpreturn array(asset_manager => array( resolver_configs => array( map => array( css/primo.css => __DIR__./../assets/css/primo.css, css/secondo.css => __DIR__./../assets/css/secondo.css, ), collections => array( css/merge.css => array( css/primo.css,css/secondo.css,),),),),), ... layout.php echo $this->headLink() ->prependStylesheet($this->basePath() . /css/merge.css);
  66. 66. Ora il modulo è davvero riusabile!66
  67. 67. 3) ENTITA’ CONDIVISE
  68. 68. Catalogo Carrello68
  69. 69. Catalogo Carrello moduli Prodotto (con la sua entità del modello, i mappers, i servizi, ecc.)69
  70. 70. Catalogo Carrello moduli Prodotto - id - nome - prezzo70
  71. 71. Catalogo Carrello ? ? Prodotto - id - nome - prezzo71
  72. 72. Modulo «carrello», vorrei: • Vederlo all’opera standalone, senza altre dipendenze72
  73. 73. Modulo «carrello», vorrei: • Vederlo all’opera standalone, senza altre dipendenze • Poter gestire prodotti diversi73
  74. 74. Modulo «carrello», vorrei: • Vederlo all’opera standalone, senza altre dipendenze • Poter gestire prodotti diversi • Poter definire dei «vincoli» su logica e campi (almeno nome + prezzo)74
  75. 75. Il carrello vuole un prodotto con certe caratteristiche...75
  76. 76. Carrello Prodotto - id - nome - prezzo76
  77. 77. Catalogo Carrello Prodotto Prodotto - id - id - nome - nome - prezzo - prezzo - dimensioneSchermo - RAM - prezzoOfferta - …77
  78. 78. Catalogo Carrello Prodotto - id - nome - prezzo - dimensioneSchermo - RAM - prezzoOfferta - …78
  79. 79. Come? Con il Service Manager/* Modulo Carrello – module.config.php */return array( router => array(…), controllers => array(…), service_manager => array( invokables => array( EcommerceService => CarrelloServiceEcommerceService, ProductService => CarrelloServiceProductService, ), factories => array(…) ));
  80. 80. ProductService/* Modulo Carrello – EcommerceServiceProductService */class ProductService implements ... { public function getProduct($i_product) { $I_repository = $this->getEntityManager() ->getRepository(CarrelloEntityCartproduct); return $I_repository->find($i_product); }}
  81. 81. Nel modulo «catalogo»? Override!/* Modulo Catalogo – module.config.php */return array( router => array(…), controllers => array(…), service_manager => array( invokables => array( ProductService => CatalogServiceMyProductService, ), factories => array(…) ));
  82. 82. Nel modulo «catalogo»? Override!/* Modulo Catalogo – module.config.php */return array( router => array(…), controllers => array(…), service_manager => array( invokables => array( ProductService => CatalogServiceMyProductService, ), factories => array(…) ));
  83. 83. E i «vincoli» (es. nome + prezzo)?
  84. 84. E i «vincoli» (es. nome + prezzo)?Ereditarietà di classe/** * CatalogEntityProduct * ...*/class Product extends EcommerceEntityCartproduct {}O implementando interfaccia/** * CatalogEntityProduct * ...*/class Product implements EcommerceEntityCartproductInterface {}
  85. 85. Catalogo Carrello Prodotto id, nome, prezzo dimensioneSchermo, RAM, prezzoOfferta, … Servizi, mapper, comportamenti, …85
  86. 86. Ora il modulo è davvero riusabile!86
  87. 87. PROBLEMA 4: PANNELLO ADMIN
  88. 88. Ho bisogno di un’altra cartella /public?88
  89. 89. Ho bisogno di un’altra cartella /public? Probabilmente no (a meno di casi particolari)89
  90. 90. Creo un modulo per tutta la parte amministrativa?90
  91. 91. Creo un modulo per tutta la parte amministrativa? Potrei… ma non sarebbe riusabile (né lui né gli altri moduli)91
  92. 92. Inglobo la parte amministrativa dentro ciascun modulo?92
  93. 93. Inglobo la parte amministrativa dentro ciascun modulo? Esatto! O almeno nella maggioranza dei casi93
  94. 94. Da dove eravamo partiti… Sito Web Contenuti Catalogo Form e pagine servizi contatti Modello statiche Amministrazione sito Web94
  95. 95. Ciò che voglio: http://mio.si.to/admin http://mio.si.to/admin/catalog http://mio.si.to/admin/user http://mio.si.to/admin/purchase …95
  96. 96. Come? Un modulo di96
  97. 97. 1. Attivazione modulo/* application.config.php */return array( modules => array(…), ZfcAdmin, Application, ),);
  98. 98. 2. Creazione controller98
  99. 99. 3. Configurazione routing/* Modulo Catalog – module.config.php */return array( router => array( zfcadmin => array( child_routes => array( catalog => array( type => Literal, options => array( route => /catalog, defaults => array( controller => CatalogControllerAdmin, action => index, ), ),),),), ),);
  100. 100. 3. Configurazione routing/* Modulo Catalog – module.config.php */return array( router => array( zfcadmin => array( child_routes => array( catalog => array( type => Literal, options => array( route => /catalog, defaults => array( controller => CatalogControllerAdmin, action => index, ), ),),),), ),);
  101. 101. 3. Configurazione routing/* Modulo Catalog – module.config.php */return array( router => array( zfcadmin => array( child_routes => array( /admin/catalog catalog => array( type => Literal, options => array( route => /catalog, defaults => array( controller => CatalogControllerAdmin, action => index, ), ),),),), ),);
  102. 102. Il pannello admin del modulo è pronto!102
  103. 103. Il pannello admin del modulo è pronto! e l’autenticazione?103
  104. 104. Autenticazione +104
  105. 105. Gestione autorizzazioni/* Modulo AppCatalog – module.config.php */return array( … bjyauthorize => array( guards => array( BjyAuthorizeGuardRoute => array( array(route => zfcadmin/catalog, roles => array(user)), ), ), ),);
  106. 106. Tiriamo le somme106
  107. 107. + RIUSO
  108. 108. APPLICAZIONI CUSTOM
  109. 109. Grazie per l’attenzioneStefano Valle@stefanovalles.valle@mvassociati.it
  110. 110. DOMANDE?
  111. 111. Photo Credits • http://www.flickr.com/photos/10459273@N05/4771563267 • http://www.flickr.com/photos/vittoriomilanes/8286559013 • http://www.flickr.com/photos/criminalintent/5101528210 • http://www.flickr.com/photos/billward/5626976800 • http://www.flickr.com/photos/frikitiki/3751108796 • http://www.flickr.com/photos/billward/5626389173 • http://www.flickr.com/photos/jlz/4275224658 • http://www.flickr.com/photos/slackpics/4289782818 • http://www.flickr.com/photos/pullip_junk/6624713631 • http://www.flickr.com/photos/nasahqphoto/5162244810 • http://www.flickr.com/photos/comedynose/5340477326 • http://www.flickr.com/photos/tracyleephoto/8322509672 • http://www.flickr.com/photos/michelleundihrefotos/5970015736 • http://www.flickr.com/photos/hefhoover/3929261255 • http://www.flickr.com/photos/89544908@N00/7877685112/113
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×