Jakie są najlepsze praktyki tworzenia dużej aplikacji Meteor z wieloma plikami szablonów HTML? [zamknięte]

We wszystkich przykładach (leaderboard, gra słów, itp.) mają jeden pojedynczy plik szablonu HTML. Czy istnieje jakiś duży projekt open source Meteor z wieloma różnymi plikami szablonów HTML, których możemy użyć jako przykład najlepszych praktyk? Nie wydaje się praktyczne, aby umieścić wszystko, czego potrzebuje duża aplikacja w jednym pliku szablonu.

 165
Author: Coleman, 2012-04-12

14 answers

/ Align = "left" / Z docs:

> HTML files in a Meteor application are treated quite a bit differently
> from a server-side framework. Meteor scans all the HTML files in your
> directory for three top-level elements: <head>, <body>, and
> <template>. The head and body sections are seperately concatenated
> into a single head and body, which are transmitted to the client on
> initial page load.
> 
> Template sections, on the other hand, are converted into JavaScript
> functions, available under the Template namespace. It's a really
> convenient way to ship HTML templates to the client. See the templates
> section for more.
 15
Author: mdgrech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 22:22:55

Jak w nieoficjalnym FAQ meteor, myślę, że to wyjaśnia, jak skonstruować dużą aplikację:

Gdzie mam umieścić moje pliki?

Przykładowe aplikacje w Meteorze są bardzo proste i nie zapewniają zbyt dużego wglądu. Oto moje obecne myślenie na temat najlepszego sposobu, aby to zrobić: (wszelkie sugestie/ulepszenia są bardzo mile widziane!)

lib/                       # <- any common code for client/server.
lib/environment.js         # <- general configuration
lib/methods.js             # <- Meteor.method definitions
lib/external               # <- common code from someone else
## Note that js files in lib folders are loaded before other js files.

collections/               # <- definitions of collections and methods on them (could be models/)

client/lib                 # <- client specific libraries (also loaded first)
client/lib/environment.js  # <- configuration of any client side packages
client/lib/helpers         # <- any helpers (handlebars or otherwise) that are used often in view files

client/application.js      # <- subscriptions, basic Meteor.startup code.
client/index.html          # <- toplevel html
client/index.js            # <- and its JS
client/views/<page>.html   # <- the templates specific to a single page
client/views/<page>.js     # <- and the JS to hook it up
client/views/<type>/       # <- if you find you have a lot of views of the same object type
client/stylesheets/        # <- css / styl / less files

server/publications.js     # <- Meteor.publish definitions
server/lib/environment.js  # <- configuration of server side packages

public/                    # <- static files, such as images, that are served directly.

tests/                     # <- unit test files (won't be loaded on client or server)

W przypadku większych aplikacji funkcjonalność Dyskretna może być podzielona na podkatalogi, które same są zorganizowane za pomocą tego samego wzór. Chodzi o to, że w końcu moduł funkcjonalności może zostać uwzględniony w oddzielnym inteligentnym pakiecie, a najlepiej udostępniony wokół.

feature-foo/               # <- all functionality related to feature 'foo'
feature-foo/lib/           # <- common code
feature-foo/models/        # <- model definitions
feature-foo/client/        # <- files only sent to the client
feature-foo/server/        # <- files only available on the server

Dowiedz się więcej: nieoficjalne FAQ Meteor

 273
Author: yagooar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-30 18:02:15

Zgadzam się z yagooarem, ale zamiast:

client/application.js

Użycie:

client/main.js

Main.* pliki są ładowane jako ostatnie. Pomoże to upewnić się, że nie masz żadnych problemów z zamówieniem obciążenia. Zobacz dokumentację Meteor, http://docs.meteor.com/#structuringyourapp , Po Więcej Szczegółów.

 36
Author: pwcremin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-29 15:12:43

Meteor został zaprojektowany tak, aby Twoja aplikacja wyglądała tak, jak chcesz. Więc jeśli nie podoba Ci się Twoja struktura, możesz po prostu przenieść plik do nowego katalogu, a nawet podzielić jeden plik na wiele kawałków, i to praktycznie tak samo. Zwróć uwagę na specjalne traktowanie katalogów client, server i public, jak określono na głównej stronie dokumentacji: http://docs.meteor.com/.

Po prostu złożenie wszystkiego w jednym wypełnieniu HTML na pewno nie pojawi się jako najlepsza praktyka.

Oto przykład jednej z możliwych struktur: w jednej z moich aplikacji, forum dyskusyjne, organizuję według modułu lub " typu strony "(Strona główna, forum, temat, komentarz), putting .css,html, oraz .plik js dla każdego typu strony razem w jednym katalogu. Mam też moduł" base", który zawiera common .css i .kod js i szablon główny, który używa {{renderPage}} do renderowania jednego z pozostałych modułów w zależności od routera.

my_app/
    lib/
        router.js
    client/
        base/
            base.html
            base.js
            base.css
        home/
            home.html
            home.js
            home.css
        forum/
            forum.html
            forum.js
            forum.css
        topic/
            topic.html
            topic.js
            topic.css
        comment/
            comment.html
            comment.js
            comment.css

Można również zorganizować przez function

my_app/
    lib/
        router.js
    templates/
        base.html
        home.html
        forum.html
        topic.html
        comment.html
    js/
        base.js
        home.js
        forum.js
        topic.js
        comment.js
    css/
        base.css
        home.css
        forum.css
        topic.css
        comment.css

Mam nadzieję, że pojawią się bardziej konkretne struktury najlepszych praktyk i konwencje nazewnictwa.

 26
Author: Jonathan Warden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-11 17:04:08

Dla wszystkich, którzy Googlują w tym temacie:

Narzędzie linii poleceń em (przez EventedMind, ludzi stojących za żelaznym routerem) jest bardzo pomocne podczas ustawiania nowej aplikacji Meteor. Stworzy ładną strukturę plików / folderów. Jeśli już pracujesz nad aplikacją i chcesz ją przeorganizować, po prostu skonfiguruj nowy projekt za pomocą em i możesz go użyć jako inspiracji.

Zobacz: https://github.com/EventedMind/em

I tutaj: https://stackoverflow.com/questions/17509551/what-is-the-best-way-to-organize-templates-in-meteor-js

 14
Author: Mikael Lirbank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:43

Myślę, że struktura plików z książki Discover Meteor jest naprawdę dobra i solidna.

/app: 
 /client
   main.html
   main.js
 /server 
 /public
 /lib
 /collections
  • kod w katalogu / server działa tylko na serwerze.
  • kod w katalogu / client działa tylko na kliencie.
  • Wszystko inne działa zarówno na kliencie, jak i na serwerze.
  • pliki w / lib są ładowane przed czymkolwiek innym.
  • każdy główny.* plik jest ładowany po wszystkim innym.
  • twoje zasoby statyczne (czcionki, obrazy itp.) go in the / public katalog.
 11
Author: Almog Koren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-14 19:20:13

Tworzenie pakietów

Oczywiście nie wszystko pasuje do tego podejścia, ale w dużych aplikacjach będziesz miał wiele funkcjonalności, które można odizolować. Wszystko, co jest rozdzielne i wielokrotnego użytku, mieści się w pakietach, reszta idzie w zwykłej strukturze katalogów, jak wspomniano w innych odpowiedziach. Nawet jeśli nie tworzysz pakietów, aby uniknąć napowietrznych, struktura kodu w sposób modułowy jest dobrym pomysłem (Zobacz te sugestie)

Meteor umożliwia drobnoziarnistą kontrolę nad ładowaniem Twoje pliki (Ładowanie zamówienia, gdzie: klient / serwer / oba) i co eksportuje pakiet.

Szczególnie przydatny jest łatwy sposób dzielenia logiki między powiązanymi plikami. Powiedzmy na przykład, że chcesz utworzyć jakąś funkcję util i używać jej w różnych plikach. Po prostu uczynisz go "globalnym" (bez var), a Meteor zawiąże go w przestrzeni nazw pakietu, więc nie zanieczyści globalnej przestrzeni nazw

Tutaj jest oficjalny doc

 9
Author: Bogdan D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-03 10:36:21

Po jakimś czasie od kodowania meteorjs, cieszę się, że mogę poświęcić trochę wolnego czasu na zbudowanie dość skomplikowanej gry online. Struktura aplikacji była jednym z moich pierwszych problemów i wygląda na to, że kilku bardzo dobrych programistów opowiedziało się za metodą strukturyzacji aplikacji, która pozwala luźno łączyć funkcjonalnie różne pakiety. Istnieją inne zalety podejścia, a 2 bardzo dobre artykuły wyjaśniające podejście można znaleźć tutaj:

Http://www.matb33.me/2013/09/05/meteor-project-structure.html http://www.manuel-schoebel.com/blog/meteorjs-package-only-app-structure-with-mediator-pattern

 6
Author: ljelewis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-05 01:36:58

Mamy duży projekt (prawdopodobnie jeden z największych projektów Meteor, jaki ktokolwiek zbudował do tej pory, ponieważ był w pełnym wymiarze czasu pracy przez 1,5 roku). Używamy tego samego zestawu nazw plików w każdym widoku. Jest to bardzo spójne i pomaga nam szybko przejść do dokładnie tego, czego szukamy:

  • wydarzenia.js
  • Pomocnicy.js
  • szablony.html
  • trasy.js
  • Style.less
  • itd.

Wygląda tak w projekcie:


       ├── consolidationRequests
       │   ├── events.js
       │   ├── helpers.js
       │   ├── routers.js
       │   └── templates.html
       ├── customerSpoof
       │   └── routers.js
       ├── dashboard
       │   ├── events.js
       │   ├── helpers.js
       │   ├── onDestroyed.js
       │   ├── onRendered.js
       │   ├── routers.js
       │   └── templates.html
       ├── emailVerification
       │   ├── events.js
       │   ├── helpers.js
       │   ├── routers.js
       │   └── templates.html
       ├── loading
       │   ├── styles.css
       │   └── templates.html
       ├── mailbox
       │   ├── autoform.js
       │   ├── consolidationRequestConfirmation
       │   │   ├── events.js
       │   │   ├── helpers.js
       │   │   ├── onCreated.js
       │   │   ├── onRendered.js
       │   │   └── templates.html
       │   ├── events.js
       │   ├── helpers.js

Powiązane szablony są po prostu przechowywane razem w tym samym pliku. Zawartość view/order/checkout/templates.html pokazana tutaj:

<template name="orderCheckout"></template>

<template name="paymentPanel"></template>

<template name="orderCheckoutSummary"></template>

<template name="paypalReturnOrderCheckout"></template>

Używamy podfolderów, gdy widoki stają się złożone z wielu części:

       ├── cart
       │   ├── addItem
       │   │   ├── autoform.js
       │   │   ├── events.js
       │   │   ├── helpers.js
       │   │   ├── onRendered.js
       │   │   ├── routers.js
       │   │   ├── styles.less
       │   │   └── templates.html
       │   ├── checkout
       │   │   ├── autoform.js
       │   │   ├── events.js
       │   │   ├── helpers.js
       │   │   ├── onRendered.js
       │   │   ├── routers.js
       │   │   └── templates.html
       │   └── view
       │       ├── autoform.js
       │       ├── deleteItem
       │       │   ├── events.js
       │       │   ├── helpers.js
       │       │   └── templates.html
       │       ├── editItem
       │       │   ├── autoform.js
       │       │   ├── events.js
       │       │   ├── helpers.js
       │       │   └── templates.html
       │       ├── events.js
       │       ├── helpers.js
       │       ├── onDestroyed.js
       │       ├── onRendered.js
       │       ├── routers.js
       │       ├── styles.less
       │       └── templates.html

Tworzymy również WebStorm, niezwykle wydajny i elastyczny edytor do tworzenia Meteor. Uważamy, że jest to niezmiernie pomocne podczas wyszukiwania i organizowania naszego kodu i produktywnej pracy. WebStorm view

Chętnie udostępnię szczegóły na życzenie.

 6
Author: Max Hodges,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-30 11:51:04

Użyj rusztowania iron-cli CLI. To bardzo ułatwia sprawę.

Https://github.com/iron-meteor/iron-cli

Po zainstalowaniu. użyj iron create my-app, aby utworzyć nowy projekt. Stworzy dla Ciebie następującą strukturę. Możesz również użyć tego w istniejących projektach. użyj iron migrate w katalogu projektu.

my-app/    
 .iron/    
   config.json    
 bin/    
 build/    
 config/    
   development/    
     env.sh    
     settings.json    
 app/    
   client/    
     collections/    
     lib/    
     stylesheets/    
     templates/    
     head.html    
   lib/    
     collections/    
     controllers/    
     methods.js    
     routes.js    
   packages/    
   private/    
   public/    
   server/    
     collections/    
     controllers/    
     lib/    
     methods.js    
     publish.js    
     bootstrap.js
 5
Author: ravish.hacker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-22 13:12:11

Podążam za formatem mattdeom boilerplate, który zawiera już iron router & Model (Collection2) . Patrz poniżej:

client/                 # Client folder
    compatibility/      # Libraries which create a global variable
    config/             # Configuration files (on the client)
    lib/                # Library files that get executed first
    startup/            # Javascript files on Meteor.startup()
    stylesheets         # LESS files
    modules/            # Meant for components, such as form and more(*)
    views/              # Contains all views(*)
        common/         # General purpose html templates
model/                  # Model files, for each Meteor.Collection(*)
private/                # Private files
public/                 # Public files
routes/                 # All routes(*)
server/                 # Server folder
    fixtures/           # Meteor.Collection fixtures defined
    lib/                # Server side library folder
    publications/       # Collection publications(*)
    startup/            # On server startup
meteor-boilerplate      # Command line tool
 4
Author: Rudy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-21 13:03:14

Istnieje wiele różnych podejść do struktury aplikacji. Na przykład, jeśli masz router i różne szablony stron, a wewnątrz każdego szablonu strony masz wiele części strony i tak dalej, chciałbym struktura to zależy od semantyki z wyższego > niższego poziomu..

Na Przykład:

client
  views
    common
      header
        header.html
        header.js
        header.css
      footer
        footer.html
        footer.js
        footer.css
    pages
      mainPage
        mainPage.html
        mainPage.js
        mainPage.css
        articles
          articles.html
          articles.js
          articles.css
        news
          news.html
          news.js
          news.css
     ...

Oczywiście możesz umieścić swoje szablony wiadomości we wspólnym folderze, ponieważ możesz użyć szablonu wiadomości na różnych stronach.

Myślę, że to najlepszy sposób, aby uporządkować swoją aplikację w sposób, w jaki czujesz się komfortowo.

Napisałem tu małą aplikację: http://gold.meteor.com I jest tak mały, że używam tylko jednego pliku html i tylko jednego szablonu.plik js.. :)

Mam nadzieję, że to trochę pomoże

 3
Author: Boris Kotov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-05 13:51:37

Istnieje nowa klasa Evented Mind o nazwie Setting Up Meteor Projects , która zajmuje się tym, ale także mówi o konfiguracji projektu i konfiguracji środowiska programistycznego.

Z struktura aplikacji wideo w klasie: Meteor nie ma zbyt mocnego zdania na temat struktury aplikacji, ale oto kilka zasad:

1) Ładowanie kolejności-Meteor najpierw trafia do najgłębszego miejsca w katalogu plików i przetwarza pliki w porządku alfabetycznym

2) klient i serwer to specjalne foldery rozpoznające

Nasza struktura wygląda tak:

both/
    collections/
        todos.js
    controllers/
        todos_controller.js
    views/
        todos.css
        todos.html
        todos.js
    app.js - includes routes
client/
    collections/
    views/
        app.js
server/
    collections/
    views/
        app.js
packages/
public/

Todos_controller rozszerza RouteController, coś, co jest dostarczane z żelaznym routerem.

Wspomniane powyżej Narzędzie em również otrzymuje teraz dużą aktualizację i powinno być znacznie lepsze i dostępne pod adresem: https://github.com/EventedMind/em

 3
Author: katogeaver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-12 22:46:54

Szukam również najlepszych praktyk, aby ulepszać i skalować moje aplikacje poprzez dobrze pomyślaną architekturę. Wszystkie powyższe praktyki działają dla małych i średnich aplikacji, ale nie powiedzie się, gdy pracujesz w większym zespole. Jest kilka sposobów, które próbowałem:

1) zastosowałem się do tej strategii: https://github.com/aldeed/meteor-autoform do skalowania i ponownego wykorzystania szablonów. Autor ma bardzo dobry pomysł na komponenty i projektowanie pola. Obecnie wdrażam go, ponieważ społeczność opracowała 36 pakietów, które obejmują prawie każdy przypadek i mogę używać TypeScript , aby być bezpiecznym w fazie rozwoju.

<template name="autoForm">
  {{#unless afDestroyUpdateForm this.id}}
  {{! afDestroyUpdateForm is a workaround for sticky input attributes}}
  {{! See https://github.com/meteor/meteor/issues/2431 }}
  <form {{atts}}>
    {{> Template.contentBlock ..}}
  </form>
  {{/unless}}
</template>

Oto dobry wpis na blogu, Jak to zrobić: http://blog.east5th.co/2015/01/13/custom-block-helpers-and-meteor-composability / jak również tutaj: http://meteorpedia.com/read/Blaze_Notes

2) ten wygląda tak obiecująco, ale nie był ostatnio aktualizowany. Jest to pakiet napisany w Coffee script o nazwie. Blaze Komponenty ( https://github.com/peerlibrary/meteor-blaze-components ) dla Meteor to system do łatwego tworzenia złożonych elementów interfejsu użytkownika, które należy ponownie wykorzystać w aplikacji Meteor. Możesz ich używać w CoffeeScript, vanilla JavaScript i ES6. Najlepsze jest to, że komponenty są OOP. Oto jeden z ich przykładów:

class ExampleComponent extends BlazeComponent {
  onCreated() {
    this.counter = new ReactiveVar(0);
  }

  events() {
    return [{
      'click .increment': this.onClick
    }];
  }

  onClick(event) {
    this.counter.set(this.counter.get() + 1);
  }

  customHelper() {
    if (this.counter.get() > 10) {
      return "Too many times";
    }
    else if (this.counter.get() === 10) {
      return "Just enough";
    }
    else {
      return "Click more";
    }
  }
}

ExampleComponent.register('ExampleComponent');

{{> ExampleComponent }}

3) lubię typy i transpilery, które mówią mi, gdzie i kiedy coś pójdzie nie tak. Używam maszynopisu do pracy z Meteorem i znalazłem następujące repozytorium: https://github.com/dataflows/meteor-typescript-utils wygląda na to, że twórca próbował osiągnąć podejście MVC.

class MainTemplateContext extends MainTemplateData {
    @MeteorTemplate.event("click #heybutton")
    buttonClick(event: Meteor.Event, template: Blaze.Template): void {
        // ...
    }

    @MeteorTemplate.helper
    clicksCount(): number {
        // ...
    }
}

class MainTemplate extends MeteorTemplate.Base<MainTemplateData> {
    constructor() {
        super("MainTemplate", new MainTemplateContext());
    }

    rendered(): void {
        // ...
    }
}

MeteorTemplate.register(new MainTemplate());

<template name="MainTemplate">
    <p>
        <input type="text" placeholder="Say your name..." id="name">
        <input type="button" value="Hey!" id="heybutton">
    </p>
    <p>
        Clicks count: {{ clicksCount }}
    </p>

    <p>
        <ul>
            {{#each clicks }}
                <li> {{ name }} at <a href="{{pathFor 'SingleClick' clickId=_id}}">{{ time }}</a></li>
            {{/each}}
        </ul>
    </p>
</template>

Niestety, ten projekt nie jest utrzymywany ani aktywnie rozwijany.

4) i myślę, że o tym już wspomniano, można skalować za pomocą pakietów. To wymaga dobrego abstrakcyjnego myślenia. Wygląda na to, że działa na Teleskop: https://github.com/TelescopeJS/Telescope

5) meteor-template-extension – zapewnia różne sposoby kopiowania helperów szablonów, procedur obsługi zdarzeń i hooków między szablonami, umożliwiając ponowne użycie kodu; minusem jest to, że całe kopiowanie musi być obsługiwane przez dewelopera, często wielokrotnie, co staje się problematyczne wraz z rozwojem bazy kodowej; ponadto bez jasno zdefiniowanej społeczności API nie można budować i udostępniać komponentów {]}

6) Komponenty Flow - Komponenty Flow są bliższe Reactowi w projekcie API, podczas gdy Komponenty Blaze zachowują znane koncepcje Komponenty Flow nadal używają szablonowych procedur obsługi zdarzeń, podczas gdy Komponenty Blaze czynią je metodami klasowymi, dzięki czemu łatwiej jest je rozszerzać lub nadpisywać poprzez dziedziczenie; ogólnie Komponenty Blaze wydają się być bardziej zorientowane na OOP; Komponenty Flow nie są jeszcze oficjalnie wydane (napisy tekstowe dla #5 i # 6 https://github.com/peerlibrary/meteor-blaze-components#javascript-and-es6-support)

Liczby 2 i 3 potrzebują niektóre też się przyzwyczajają, ale z czasem zyskasz szybkość rozwoju. Numer cztery pozwala budować i testować komponenty, aby Kod był bardziej stabilny. Numer trzy ma zaletę pełnego bezpieczeństwa typowego maszynopisu, co jest ogromnym plusem, gdy rozwijasz się w zespole ze słabą dokumentacją. Jednak obecnie przenoszę numer dwa do maszynopisu, ponieważ czuję się bardzo komfortowo z nim pracować i nie muszę tweetować pakietu kompilatora, aby działał z Meteorem, gdy nie używam Łyk.

Wciąż trudno jest znaleźć odpowiedni sposób pracy z Meteorem. Musisz sam to rozgryźć, w przeciwnym razie skończysz z ładnie ułożoną strukturą folderów, ale nie masz pojęcia, gdzie wszystko jest. Szczęśliwego kodowania.
 1
Author: MichelH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-19 19:20:13